A concise but complete inventory of all work Will (fucory) has built
| Repo | GitHub | Layer | Stack |
|---|---|---|---|
| plue (private) | github.com/codeplaneapp/plue | Code hosting + execution substrate: sandboxes, workspaces, agent sessions, git/jj protocol, billing, .jjhub workflows |
Go API + Rust jj-FFI + SolidJS UI + Postgres + gVisor / Freestyle VMs |
| smithers | github.com/smithersai/smithers | Declarative orchestration engine: workflows, scheduler, agent adapters, time-travel, gateway, observability | TypeScript + Effect.io + SQLite + JSX-DSL |
| gui | github.com/smithersai/gui | Native control plane + FFI runtime + Python agent harness | Swift macOS/iOS + Zig libsmithers + Python FastAPI |
Naming note.
jjhubwas the original name; and has gone by plue / Smithers Cloud / Codeplane depending on context. The.jjhub/folder convention in repos kept its name for compatibility.
1. The declarative orchestration layer — smithersai/smithers
The "Terraform for software factories" core. JSX-based workflow DSL on a custom Effect.io scheduler. Not Temporal — we wrote our own.
It's currently TypeScript only but we have made functional proof of concept showing how it's intermediate data structure could be extended to python and other languages.
Smithers has extremely high user satisfaction. Many users who try it convert to becoming power users and advocates
Workflows are TypeScript/JSX components with Zod-typed outputs. They work similar to React which hacks into the models reinforcement learning. Models today are already great at smithers:
smithers((ctx) => (
<Workflow name="bugfix">
<Sequence>
<Task id="analyze" output={outputs.analyze} agent={analyzer}>…</Task>
<Task id="fix" output={outputs.fix} agent={fixer}>…</Task>
</Sequence>
</Workflow>
))~100 example workflows in examples/ (code-review-loop.jsx, debate.jsx, dependency-update.jsx, …). All five superstructures from the memo (PR Factory, Health Monitor, Spec Pipeline, Recruiting, Incident Response) are composable from the existing catalog today.
Node catalog (packages/components)
- Control flow:
<Sequence>,<Parallel>,<Branch>,<Ralph>(bounded loop),<TryCatchFinally>,<ContinueAsNew>,<Subflow> - Human-in-the-loop:
<Approval>/<ApprovalGate>,<HumanTask>,<Signal>,<WaitForEvent>,<Timer> - Higher-order patterns:
<ReviewLoop>,<MergeQueue>,<ClassifyAndRoute>,<Debate>,<DecisionTable>,<EscalationChain>,<GatherAndSynthesize>,<Kanban>,<Optimizer>,<Panel>,<Poller>,<Runbook>,<Saga>,<ScanFixVerify>,<Supervisor>,<Worktree>,<Sandbox>
packages/scheduler— pure decision function:(TaskState, outputs) → EngineDecision[]packages/engine— Effect.io execution (@effect/workflow,@effect/cluster,@effect/sql-sqlite-bun)packages/driver— consumes decisions, runs tasks, reports resultspackages/db— SQLite via Drizzle: event log, task state, approval requests, alerts, dynamic per-workflow output tables
Agent abstraction = our BYOH (packages/agents)
BaseCliAgentwraps any CLI harness (Claude Code, Codex, …) the way k8s wraps containers- AI SDK adapters: Anthropic, OpenAI, Pi, Kimi via
@ai-sdk/*= our BYOM - Capability registry declares tools/limits per agent
- Auto-detects installed CLI agents from
$PATH packages/accountsmanages multi-account Claude/Codex/Gemini subscriptions
- Time-travel (
packages/time-travel+packages/vcs) — snapshots, diffs, fork-from-checkpoint, replay, revert via jj - Gateway (
packages/gateway+gateway-client+gateway-react) — typed RPC, auth scopes, OpenAPI defs, WebSocket event streams - Observability (
packages/observability+apps/observability) — OpenTelemetry-native, Prometheus, OTLP, pre-built Grafana dashboards - Memory (
packages/memory) — semantic recall, namespaced facts, vector-ready - Scorers (
packages/scorers) — LLM judges, aggregation, persistence — substrate for auto-eval / self-improvement loops - MCP server (
apps/cli/src/mcp/semantic-server.js) — exposes Smithers operations as MCP tools so Claude/Cursor/Codex can orchestrate Smithers itself
CLI (apps/cli, 50+ commands)
init / up / down / supervise / ps / logs / events / inspect / node / why / chat / chat-create / hijack / human / approve / deny / signal / fork / replay / revert / timetravel / retry-task / alerts / observe / graph / agent {add,list,test,doctor} / accounts {list,remove} / memory / schedule {add,list,rm,start} / openapi {preview,issue,revoke} / workflow {run,list,create,doctor}
The memo's "shell into a running agent" = smithers hijack + smithers signal + smithers chat. Already shipped.
2. The hosting + execution substrate — codeplaneapp/plue (private)
This is the production layer the memo treats as a black box ("compute is flexible — sandbox, pod, GPU, or VM"). We've built all of it: code hosting, CI workflows, agent sandboxes, cloud workspaces, billing, GitHub sync.
server— Chi-based Go API (Postgres, OTEL, SSE)ssh— gliderlabs git-over-SSHrepo-host— git proxy + jj-FFI via Rust (smithers-ffi)runner— Postgres task queue (FOR UPDATE SKIP LOCKED) → gVisor sandbox managerguest-agent— runs inside sandbox, vsock to hostws-runner— Freestyle VM lifecycle for full workspaceselectric-proxy— ElectricSQL real-time sync bridgesmithers— the Go CLI
| Group | Examples |
|---|---|
| Auth | Sign-in-with-Key (ECDSA), GitHub OAuth, WorkOS SSO, Auth0, OAuth2 server, SSE tickets |
| Repos | /api/repos/{owner}/{repo} — metadata, contents, git trees/commits, jj changes/diffs |
| Landing requests | jj-native stacked PRs — /landings/{n}, /diff, /conflicts, /reviews, /comments |
| Stacks & bookmarks | jj active-stack management, protected bookmarks |
| Issues | GitHub-compatible — issues, comments, reactions, dependencies, pinning, artifacts |
| Workflows | /actions/runs/{id}, /workflows/runs/{id}/nodes/{nodeId}, cache/artifact APIs |
| Agents | /agent/sessions/{id} — messages, streaming SSE, events |
| Workspaces | Freestyle VM provisioning, /workspaces/{id}/ssh, snapshots, sessions |
| Orgs/teams | members, teams, permissions |
| Billing | Stripe — checkout, refresh, webhooks; tiers, entitlements, usage counters, credit ledger |
| Search | full-text repo + issue search (Postgres FTS) |
| Integrations | MCP registry (/api/integrations/mcp), AI skills (/api/integrations/skills), Linear sync |
| Webhooks | GitHub App, Linear, Stripe, canary, user-registered repo webhooks (see §3) |
| Internal | runner register/claim/heartbeat, task stream/complete, push events |
| Git smart HTTP | /info/refs, /git-upload-pack, /git-receive-pack |
- gVisor sandboxes for ephemeral task execution (limited syscalls, vsock host comms)
- Freestyle VMs for full cloud workspaces (SSH, snapshots, fork-for-parallel-exploration, suspend/resume)
- Both share the agent-session abstraction
- Helm charts (
infra/helm/smithers,infra/helm/electric, cert-manager) - Terraform for GCP (Cloud SQL Postgres, GCS, GKE)
- k6 load tests
SolidJS + Tailwind. Repo browser, issues, landing requests, workflow runs, agent chat, workspaces. SSE for real-time updates. Built-in Ghostty terminal. Plus docs, marketing, admin, github-sync, notion-sync.
This is purely a POC WIP. The webui is meant to work exactly as the mobile app. We also have an even more powerful desktop app inp rogress.
This is what makes plue a factory substrate, not just a code host. Every repo can commit a .jjhub/workflows/*.tsx directory; plue discovers, schedules, and executes those workflows in sandboxes.
A .jjhub/workflows/ci.tsx looks like this (real example from our self-hosted CI):
import { Workflow, Task, Parallel, on } from "@smithers-ai/workflow";
import { $ } from "bun";
export default (
<Workflow
name="ci"
triggers={[
on.push({ bookmarks: ["main"], ignore: ["infra/**"] }),
on.landingRequest.opened(),
on.landingRequest.synchronize(),
on.landingRequest.readyToLand(),
]}>
<Parallel>
<Task id="lint-go">{async () => { await $`golangci-lint run --timeout=5m`; }}</Task>
<Task id="lint-rust">…</Task>
<Task id="lint-helm">…</Task>
<Task id="lint-ts">…</Task>
</Parallel>
<Parallel>
<Task id="test-go-unit">…</Task>
<Task id="test-go-integration">…</Task>
</Parallel>
</Workflow>
);The package — @smithers-ai/workflow in packages/workflow/ — re-exports Sequence, Parallel, Branch, Ralph from smithers-orchestrator and adds plue-specific Workflow and Task components plus the on.* trigger builder. It's the same engine as smithers — plue's runner instantiates smithers-orchestrator and calls runWorkflow() with SmithersCtx. The TSX in .jjhub/workflows is rendered to the Smithers workflow schema and executed by the same scheduler.
Every constructor returns a TriggerDescriptor with a _type discriminator:
on.push({ bookmarks?, tags?, ignore? })on.landingRequest.opened() | closed() | synchronize() | readyToLand() | landed()on.release.published() | updated() | deleted() | released() | prereleased()(with optionaltags)on.issue.opened() | closed() | edited() | reopened() | labeled() | assigned()on.issueComment.created() | edited() | deleted()on.schedule(cron)— cron-based schedulingon.manualDispatch(inputs?)— manual trigger with typed inputson.webhook(event)— generic event routing (anything plue receives or fires)on.workflowRun({ workflows, types? })— trigger on completion of another workflowon.workflowArtifact({ workflows?, names? })— trigger on artifact upload
id, output?, agent?, skipIf?, needsApproval?, timeoutMs?, retries?, continueOnFail?, label?, meta?, if?, cache?.
Plus first-class helpers: createWorkflowArtifactHelpers() (upload/download) and createWorkflowCacheHelpers() (save/restore — GitHub Actions cache equivalent).
- Discovery. When a repo is pushed or a workflow run is requested, plue parses
.jjhub/workflows/*.tsx.collectRegisteredWorkflowTriggers(cfg)ininternal/services/workflow_trigger_registry.gowalks each workflow'striggers={…}prop. - Persistence. Triggers are inserted into the
workflow_triggerstable:event_type(push, pull_request, pull_request_review, check_suite, check_run, stack_submit, schedule, manual),event_action(e.g. "opened", "synchronize", "published"), plusworkflow_path,workflow_definition_id,repository_id,enabled. - Matching.
ListWorkflowTriggersByRepository(repositoryID)fetches all enabled triggers for fast event-time lookup. - Scheduling. An incoming event (push, webhook, cron tick, manual dispatch) is matched against triggers and matching workflows are enqueued as
workflow_runs. - Execution. The runner pulls tasks via
POST /internal/runners/{id}/claim(PostgresFOR UPDATE SKIP LOCKED), spins up a gVisor sandbox, and the guest-agent executes the rendered Smithers graph. Logs stream via SSE intoworkflow_logs; results land inworkflow_steps/workflow_tasks.
Everything is observable in the GUI's JJHubWorkflowsView and via the /api/repos/{owner}/{repo}/actions/runs/{id} API.
All handlers live in internal/routes/ and internal/services/:
| Endpoint | Handler | Verification | What it does |
|---|---|---|---|
POST /webhooks/github |
GitHubWebhookHandler |
HMAC-SHA256 (X-Hub-Signature-256) |
Enqueues github_webhook_jobs for async processing |
POST /webhooks/linear |
LinearIntegrationHandler |
Linear SDK signature | Syncs Linear issue state to plue issues / landing requests |
POST /api/billing/webhook |
BillingHandler |
Stripe ConstructEvent() |
Updates subscription state, billing entitlements |
POST /canary/webhook-receiver/{token} |
CanaryWebhookHandler |
HMAC-SHA256 token | Receives canary deployment health checks |
GitHub webhook flow (internal/services/github_webhook.go:141):
HandleGitHubWebhook(deliveryID, eventType, signature, payload)verifies signature- Filters supported events:
push,pull_request,pull_request_review,check_suite,check_run,installation,installation_repositories - Enqueues a row in
github_webhook_jobs(delivery_id,event_type,action,installation_id,github_repository_id,payload) - Handles installation events directly (upserts
github_app_installations+github_app_installation_repositories) - A worker polls
github_webhook_jobs(status=pending), translates GitHub repo IDs to plue repo IDs viaListRepositoryIDsForGitHubWebhookJob(), then evaluatesworkflow_triggers→ enqueues matchingworkflow_runs
That last step is how on.push(...) in a .jjhub/workflows/*.tsx actually fires.
Repo-owners can register custom webhooks per repo (GitHub-style "Webhooks" tab):
- CRUD:
POST/PATCH/DELETE /repos/:owner/:repo/hooks - Model:
db.Webhook—id,repository_id,url,secret,events[],is_active - Secrets encrypted at rest via
webhookSecretCodec(AES wrapper) - Max 20 per repo
- Subscribable events:
push,pull_request,landing_request.opened,landing_request.closed,landing_request.synchronize,workflow_run.completed, etc. — same vocabulary as the trigger DSL
Delivery system (internal/webhook/):
- Queue:
webhook_deliveriestable (webhook_id,event_type,payload,status,attempts,next_retry_at) - Worker (
worker.go):PollQueue()claims due deliveries, sends HTTP POST withX-Smithers-Signature-256HMAC - Retry policy: exponential backoff, terminal failure auto-disables the webhook
- Test endpoint:
POST /repos/:owner/:repo/hooks/:id/tests— enqueues immediate delivery with sample payload - Observability: every attempt logged with
status,response_status,response_body,processed_at
This makes plue a two-way event hub: GitHub events flow in, plue events flow out, and .jjhub/workflows sit in the middle reacting to either.
5. The native control plane — smithersai/gui
Native desktop + mobile control plane plus an FFI runtime any other client could link.
Dashboard, Runs (live + inspect), Approvals, Changes/Diff, Issues, Workflows (JJHubWorkflowsView.swift), Agents, Frame Scrubber (time-travel UI), Chat (markdown segmentation + privacy redaction), embedded Ghostty terminal, SQL browser, dev telemetry, multi-workspace switcher, command palette, custom keybindings, browser surface for browser-tool output.
iOS app (ios/Sources/SmithersGUIiOS/)
Onboarding, settings, devtools live inspector, repos, workflow runs, terminal (Ghostty VT renderer), chat, approvals, OAuth2 auth.
Smithers.Client.swift + DevToolsModels.swift + DevToolsStore.swift — routes through libsmithers FFI or HTTP gateway based on connectionTransport. Snapshot/delta streaming with sequence tracking, exponential-backoff reconnect, "ghost node" LRU for unmounted task nodes, stale-banner after 2 s disconnect, audit-row tracking for cross-run memory. Live ↔ Historical(frameNo) switching is built-in.
Zig core — libsmithers (libsmithers/, build.zig)
- C ABI in
smithers.h:smithers_core_t, sessions, event callbacks (STATE_CHANGED, AUTH_EXPIRED, RECONNECT, SHAPE_DELTA, WRITE_ACK, PTY_DATA, PTY_CLOSED),write()(pessimistic mutation with audit-row futures),shape_subscribe(), PTY attach/write/close - Modules:
core/(sessions, transport, cache, schema),core/electric/(Electric protocol),core/wspty/(WebSocket PTY),devtools/(snapshot+stream client),terminal/(PTY cell rendering, SGR),workflow/(DAG parsing),persistence/,session/(zmux daemon control) - Ghostty terminal via
GhosttyKit.xcframeworkbuilt from vendored submodule
- FastAPI + Pydantic AI + MCP.
create_agent_with_mcp()with extended thinking (50K budget), 64K max output task_executor.py—task()single delegation,task_parallel()up to 10 concurrent- Agent types in
registry.py:build/plan/explore/general, each with declared tool access - Tools: file I/O (read/edit/multiedit/patch), grep, LSP (hover/diag/defs/refs/symbols), PTY exec, browser automation (snapshot/click/type/scroll/extract/navigate), web fetch, task delegation
- Git-based snapshot system (
snapshot/snapshot.py) — bare repo at.agent/snapshots/,track()→ tree SHA,restore(sha)→ reset working tree
Go SDK + TUI (sdk/agent/, tui/)
Charmbracelet Bubbletea TUI as a fallback interface; SDK wraps the Smithers API.
Slash commands (/run, /approve, /revert), command palette, frame scrubber for time-travel review, external-agent launcher (Smithers.ExternalAgent.swift — spawns claude / codex / gemini CLIs with session watcher).
| Memo primitive | Where it lives today |
|---|---|
| Declarative workflow-as-code | smithers JSX DSL + Zod outputs |
| GitHub-Actions-style repo workflows | .jjhub/workflows/*.tsx in plue, dispatched by workflow_triggers table |
| Composable modules / npm-style registry | Component catalog in packages/components; npm distribution exists, marketplace doesn't |
| BYOH (wrap harnesses like k8s wraps containers) | packages/agents BaseCliAgent + capability registry |
| BYOM | AI SDK adapters: Anthropic, OpenAI, Pi, Kimi + accounts mgmt |
| Triggers | on.push / landingRequest.* / release.* / issue.* / schedule / manualDispatch / webhook / workflowRun / workflowArtifact |
| Inbound webhooks | /webhooks/github, /webhooks/linear, /api/billing/webhook, canary — fan out to workflow triggers via github_webhook_jobs |
| Outbound webhooks | Per-repo user-registered webhooks, signed HMAC, retry queue in webhook_deliveries, test endpoint |
| Decision gates / approvals | <Approval>, <HumanTask>, durable timeouts, smithers approve/deny |
| Steering (shell-in, mid-run messages) | smithers hijack / signal / chat; GUI command palette + slash commands |
| Time-travel & forking | packages/time-travel + smithers fork/replay/timetravel/revert; GUI FrameScrubber |
| Observability / cost | OTEL + Prometheus + Grafana in packages/observability; billing/usage in plue |
| Configurable compute | gVisor sandboxes + Freestyle VMs in plue (cmd/{runner,ws-runner,guest-agent}) |
| Self-hostable OSS core | smithers + gui are self-contained and locally runnable; plue is the managed cloud |
| Managed cloud DX | plue: hosted API, billing, workspaces, runner pool, GitHub App, custom webhooks |
| Real-time control plane | Native macOS/iOS GUI + SolidJS web UI + Gateway WebSocket streams |
| Declarative policies | Gap — exists as ad-hoc config, not a top-level primitive |
| Managed secrets / network allow-deny | Repo/org secrets exist (repository_secrets, organization_secrets); network policy is a gap |
| Module marketplace | MCP/skills registry endpoints exist (/api/integrations/{mcp,skills}); distribution layer is a gap |
One-line summary. The engine, the substrate, and the control plane are all built. .jjhub/workflows/*.tsx already gives every repo a GitHub-Actions-equivalent that runs on the same Smithers scheduler as long-running agent workflows — there is no separation between "CI" and "agentic factory." What remains is the declarative policy layer, the module marketplace, and turning the substrate into a productized cloud — i.e. exactly the surface the memo argues is the next 18-month frontier.