Workbench is a client of Interchange.
The hub mounts only stock @intx/hub-api routes plus a set of Corbits
libraries, each mounted once (@corbits/mailbox, @corbits/memory,
@corbits/artifacts, @corbits/webhooks, @corbits/cron, @corbits/mcp, an
OAuth library on @corbits/oauth-core); any other hub mount is cutover debt with a Linear
issue, never a pattern to extend. A workbench is a plain
Interchange tenant, nothing more. The web client sets itself up over stock
routes on start (auth → tenant → definitions/credentials/grants); the hub
never seeds data on a client's behalf.
- Interchange is the platform. Never reimplement credential resolution,
agent launch, session orchestration, inference, ID generation, or the
workflow runtime — consume
@intx/*as published packages.vendor/intxholds hand-copied fallbacks for capabilities not yet published, tracked with kill dates in VENDORED.md; the upstream repository is never modified. - Apps stay generic; packages own the domain. A product rule inside
apps/*belongs in a package.workflows/*are plain npm-shaped packages, deployable to any stock Interchange hub through the stock deploy route — this repo does not publish them to npm. - Myra is the one default agent.
agents/myrais the assistant that uses Workbench on the person's behalf; every agent package underagents/*is trim —index.ts(plussystem-prompt.tswhen the prompt is large) — with no agent-owned artifact client/tool code of its own. - No fallbacks. Cut over cleanly — never leave a legacy path beside a
new one. Config/manifest objects are explicit literals; the one exception
is an optional key under
exactOptionalPropertyTypes(see below), where...(x !== undefined ? { k: x } : {})is the only correct way to omit it. - Parse at every trust boundary. arktype schemas for env, request
bodies, and external data; never
as Tuntrusted input. - Custom DB tables live on their own Postgres schema, with foreign keys back to Interchange's tenant/principal tables. Each package ships one idempotent migration, which the hub applies itself at boot.
- Ancestor-walkable except grants. Credentials, tools, and definitions inherit down the tenant tree; grants never do.
- This repo is public. No secrets or credentials, ever —
.env.exampleis the only tracked env file. Anything sensitive (client names, internal context, infra/deploy rulings) goes in Linear, not in commits, PRs, or docs. - Core UI components live in corbitsdev/react-ui. Build reusable components there; only workbench-specific composition lives here.
bun run check(typecheck, lint, fmt, test) must pass before every commit. There are no custom structural check scripts; a rule lives in the code or schema itself or it does not exist.- Worktrees live in
.worktrees/<branch>; branch =cl-<issue#>-<slug>. - Commit sequence per change: tests first ("Add tests for X"), then implementation ("X: what changed"), then docs ("Update docs: X"). One logical change per commit; commit messages are written for a public audience.
- Bun loads repo-root
.env, so an unset test inheritsHUB_DATA_DIR=.data/hubinside this work tree — the hub's git-on-disk init can then walk up to the enclosing.gitand land a genesis commit on the working branch. Tests that boot the hub or touch seed/deploy git paths must callinstallDisposableHubDataDir()frome2e/lib/disposable-hub-data-dir.ts; never delete theHUB_DATA_DIRkey. - Deployment mechanics are not settled enough to state here — see IMPLEMENTATION.md's Deployment section first.
Env flags (unset behavior):
| Flag | If unset |
|---|---|
DATABASE_URL |
DB-gated suites skip locally; CI=true makes it a hard failure on jobs that provision Postgres |
HUB_DATA_DIR |
hub boot fails — required runtime config |
CI |
set by GitHub Actions; not a caller flag |
E2E_PROVIDER / E2E_PROVIDER_API_KEY / OLLAMA_BASE_URL |
live-inference e2e stays on the noop/stub path |
Two homes only. A load-bearing unit test sits beside the module it covers
(src/**/*.test.ts); a load-bearing integration or DB-backed suite lives
under root e2e/<area>/, with shared helpers in e2e/lib/. No test/
directory exists anywhere — not in apps, packages, agents, or tools. A test
that isn't load-bearing (pins a literal, a shape, a re-export, or re-proves
Interchange's own behavior) is deleted, not moved.
Tests are meaningful red/green tests only — no coverage theater. An outdated test is deleted in the same PR that breaks it, not adapted to keep passing; rebuilding coverage for the area it covered happens under CL-8150.
- Report every caught error through
reportErrorfrom@corbits/error-sink— never a barecatch {}. - A package's
browser-safesubpath never imports a server-only dependency (postgres,drizzle-orm,hono, any@intx/*). - Every package needs a
LICENSEfile (LGPL-2.1). - A dependency declared in the root
catalogis consumed ascatalog:. exactOptionalPropertyTypes: true— omit an optional key rather than assigning itundefined.- A custom table that gets deleted is hard-removed: delete the schema code, no drop migration. Cutovers are breaking; reset the database.
- README.md — quickstart and repo layout
- PRODUCT.md — what Workbench is and why
- ARCHITECTURE.md — system structure
- IMPLEMENTATION.md — concrete stack, deployment, open questions
- CONTRIBUTING.md — contribution flow and CLA
- LICENSE.md — GPLv2 with AI Exception
- SECURITY.md — how to report vulnerabilities
- VENDORED.md — the vendoring ledger and its rules
- DESIGN.md — the UI design system canon; a screen that disagrees with it is wrong until a review changes the doc
- docs/GLOSSARY.md — product-term to platform-term mapping
- docs/local-dev.md — running fully local with Ollama