| name | rask-architecture |
| description | Where new code belongs in the rask workspace: the language-pure planes (Python `packages/` + `services/`, the `frontend/` bun+turbo root, sealed `runners/`) and the entrypoint contract (`make_service_app` + injectable lifespan). Use when adding, moving or deleting a workspace member, service, zone or deployable; editing a `pyproject.toml`, the root `uv.lock`, or a uv/bun workspace glob; wiring a service entrypoint; or when newly added code won't resolve — ModuleNotFoundError, a `uv sync` workspace error, or a package `bun install` silently skipped. |
rask architecture (workspace planes + composition)
The single most rask-specific thing to get right: which plane a package lives in, and how entrypoints compose packages. Get this wrong and uv/bun resolution breaks (loudly for uv, silently for bun) or you smear a domain across layers. Defers Python idioms to writing-python, FastAPI routing to fastapi, container builds to dockerfile.
There is no projects/ layer. The Polylith-style projects/<name> composition stubs were removed (2026-07). Deployables are ordinary workspace members built by .docker/<name>.dockerfile running uv sync --frozen --package <name> against the root uv.lock. Never recreate per-deployable pyprojects or per-deployable locks.
When to use
- Adding, moving, or deleting a workspace member (
packages/*, services/*, or anything under frontend/).
- Wiring a new HTTP service entrypoint or editing one.
- Editing any
pyproject.toml members/dependencies, or frontend/package.json workspaces.
- "ModuleNotFoundError" / uv won't resolve a first-party import after you added code.
- Deciding where new code belongs: reusable lib, runnable component, or deployable.
The planes — one language per plane
The tree is split by language first, then by layer. Each globbed directory is single-language; that is what makes the globs safe (see the invariants).
| Plane | Path | Rule | Has entrypoints? |
|---|
| Python packages | packages/<name> | reusable Python libraries (ray-cluster-env is deps-only: it NAMES the Ray images' environment and ships no code) | No entrypoints — the one CLI exception (ratch) died with its package at the 2026-08-28 dissolution |
| Python services | services/<name> | runnable Python: HTTP services | Yes |
| sealed runners | runners/<name> | model environments with their own pyproject.toml — and their own uv.lock only where they build an image (assist, dummy, htr); the offline Ray Data runners ship a pyproject alone and let Ray install the env on workers via runtime_env. Matched by no workspace glob | Yes (Ray entrypoints) |
| JS/TS frontend | frontend/ | its own bun + turbo workspace root (own package.json, bun.lock, turbo.json, knip.json, .oxlintrc.json, .oxfmtrc.json, patches/, assets/) | — |
| ↳ zones | frontend/microfrontends/<zone> | the 7 SvelteKit MFE apps | Yes |
| ↳ JS libraries | frontend/packages/<name> | reusable TS/Svelte libraries | No |
| scripts | scripts/ | ALL dev/ops scripts, shell and python, flat | one-shot, not a workspace member |
Current Python packages: lineage-kit, ray-cluster-env (deps-only), ray-kit, service-kit, storage, validate. (ratch was DISSOLVED 2026-08-28 — open_ray-kernel.md; tracker was DELETED 2026-08-30 — a per-file transfer ledger that never found a consumer here, its job done by JetStream WORK_QUEUE retention plus Dapr Workflow's durable state; htr is not a package — it is runners/htr, sealed and outside every glob.)
Current Python services (13): gateway, compute, controlplane, ingest, flows, notifications — plus the lance plane (catalog, lineage, medallion, maintenance, viewer, search, annotator). (notifications is the per-subject inbox behind the bell, :8850, app-id notifications — see rask-services-fleet.) (compaction was renamed maintenance in 06cc7579 — it compacts, optimizes indices, cleans up old versions and reconciles cross-store drift, so it is named for all four, not one. core/core_api/search_api/volumes_api died in the R6/R20 media wave.)
Current sealed runners (9): asr, assist, diarize, dummy, htr, insid3, kg, topics, voiceprint. (dummy is the GPU-free lane prover — real CDF read → merge_insert → fragment commit, no model download; insid3 is pinned to python 3.10.)
Current JS packages: api, config, dockview, engine, explorer-api, flow, labeling, , — see .
Current zones (7, verified against 2026-08-09): (catch-all, base ), , , , , , — each based at a bare . (on train's port 5178); a leftover on a dev host is untracked build residue, not a zone. // are ; and are routes .
Current deployables (each = a workspace member + a ): , (R22 — on every surface), , , , , — plus the one parametrized built per zone (images tagged ), (the Ray head/Serve image) and , which is ONE image for lance services (, , ×2 apps, , , , ) run with different commands. NB 's is still only — has a dockerfile and a chart Deployment () but is not in that build loop yet.
The composition seam: three app factories, one per plane
service_kit.make_service_app(*, title, routers, proxy_router=None, lifespan=None) builds the FastAPI app with shared config/handlers/middleware. The lifespan is injectable: stateless services get the minimal default_lifespan (settings only); stateful ones pass a LifespanFactory (Lance/Ray/S3). Routers mount under settings.api_prefix; proxy_router mounts at root.
⚠️ THREE FACTORIES ARE SANCTIONED — know which plane you are in. This section used to say two
layouts, and that the seven non-fleet services "construct FastAPI(...) directly with bespoke
lifespans"; that was true and is not any more. Twelve of the thirteen services now come out of a
factory, and which one is decided by the plane, not by taste:
| Plane | Factory | Services | Routers mount |
|---|
| fleet | service_kit.make_service_app | compute, controlplane, flows, ingest, notifications | under settings.api_prefix |
| media | service_kit.media.app.build_media_app | viewer, search, annotator | at the root; MediaSettings; CORS exposes the Range headers |
| lance | service_kit.lance_app.build_lance_service_app | catalog, lineage, medallion ×2 apps, maintenance | at the root; each service's own core/config.py::get_settings() |
gateway is in none of them — it builds FastAPI(...) itself, because it is a proxy, not a router
host. The media three also share ONE lifespan (service_kit.media.lifespan.make_media_lifespan),
with setup/teardown hooks for what is genuinely per-service (the annotator's actor plane).
The non-fleet services keep the fastapi skill's api/v1/endpoints/ + core/ + services/ layout
rather than the fleet's flat-module layout — that half of the old paragraph still holds. What changed
is the ENTRYPOINT: a main.py that opens its own app = FastAPI(...) is now the exception to
justify, not the norm (open_python-audit DUP-12 counted eight of them repeating one boot, and the
copies had drifted — the medallion mover had lost its request-id layer entirely).
A thin fleet-layout entrypoint is ~20 lines — import routers + a lifespan from the domain package, call the factory. compute/__init__.py:
from compute import health, proxy, routes
from compute.lifespan import make_lifespan
from service_kit import make_service_app
app = make_service_app(
title="compute",
routers=[health.router, routes.router],
proxy_router=proxy.router,
lifespan=make_lifespan,
)
The core husk is GONE (R6/R20, 2026-07-28)
services/core + services/core_api (the post-P7a transitional husk) are deleted, with search_api and volumes_api. Their capabilities live in the explorer plane: the S3 object browser is the viewer's objects.py endpoints (/api/explorer/object*); lines/EAD FTS re-land as catalog-governed Lance tables behind /api/explorer/search (docs/architecture/lance-ns-merge.md R6). Do not resurrect them.
Hard invariants (the gotchas)
- Workspace membership is globbed — and that is only safe because every globbed dir is single-language. Root
pyproject.toml has [tool.uv.workspace] members = ["packages/*", "services/*"]; frontend/package.json has workspaces = ["microfrontends/*", "packages/*"] (paths relative to frontend/). Drop a directory in the right plane and it is a member — no manifest edit. The safety condition is the language purity, and the two toolchains fail asymmetrically when it breaks: a dir under a uv glob without a pyproject.toml is a hard error (Workspace member … is missing a pyproject.toml), fixable only by an exclude list (enumeration by another name); a dir under a bun glob without a package.json is SILENTLY skipped — bun prints "Done!" and the package is simply never installed, built, linted or tested, and nothing says so. So: never put a JS package under root packages//services/, and never put a Python package under frontend/. (The root manifest also notes runners/* is deliberately matched by no glob — sealed model envs whose heavy pins must never enter the fleet's resolution.) See references/adding-a-package.md.
- One lock. The root
uv.lock is the only Python lockfile — dev, tests, and every fleet docker image resolve from it (uv sync --frozen --package <name>). The sealed runners/htr project carries its own lock and is invoked via uv run --project runners/htr runner (in-cluster the ray image ships the console script on PATH).
service-kit keeps a light base. Base deps are storage, fastapi, pydantic, pydantic-settings, python-dotenv, dapr>=1.18.1, and 8 OpenTelemetry packages — the SDK, the OTLP/HTTP exporter, and instrumentors for fastapi, httpx, logging, requests, grpc and aiohttp-client. The last three landed 2026-08-23: the fleet runs bare uvicorn with no opentelemetry-instrument launcher, so whatever setup_otel names is ALL the instrumentation it gets, and without grpc + aiohttp the app→sidecar hop carried no and every Dapr span rooted a new trace. The heavy Lance/Ray deps live behind the / / extras — keep them there. add , , or to the base: service-kit is shared by every service including the storeless ones ( via , ).
When to load each reference
| Need | Read |
|---|
Adding, moving or deleting a member or deployable — run its checklist to the end; step 4 (known-first-party) is the one that gets skipped | references/adding-a-package.md |
| The full service fleet, ports, and which package each entrypoint composes | references/service-fleet.md |
Sibling skills
rask-services-fleet (the gateway + per-service routing) · rask-frontend (zones, data, gates) · rask-styling (@rask/ui) · rask-lance-catalog (the catalog, governance, maintenance).
A runner's internals are deliberately undocumented here: each runners/<workload> is sealed and owns its
own pipeline, models and GPU packing. There is no per-workload skill — one would make that modality look
privileged, which is the opposite of how this platform is built.