Architecture
Pure pipelines. Injected world. Checkable output.
An npm-workspaces monorepo, dependency-ordered, TypeScript strict everywhere (exactOptionalPropertyTypes included), with the current count reported by npm test. The design rule that shapes everything: the pipelines are pure, and the world arrives through injected ports.
The system
The packages
| field | type | notes |
|---|---|---|
| studio-core | pure | Domain types, zod schemas, OccasionContract, the three pipelines, PolicyGate, ids. No I/O — everything worldly is a port (TextModelPort, PlacesPort, GradePort…). |
| tribunal | pure-ish | OQS v1.2 profiles and deterministic validators, the critique + repair engine, rubricAsMarkdown()/Json() — the same source that renders /standard and these docs. |
| receipts | crypto | Canonical JSON hashing, seal-leaf encoding, EIP-712 signing, the KeepsakeRegistry client. |
| contracts | solidity | KeepsakeRegistry.sol, compiler pipeline, and a cross-language test that runs the REAL bytecode in an in-process EVM to prove TS and Solidity encode identical leaves. |
| providers | adapters | Model router (OpenAI live; Anthropic/xAI wired), image generation, House Styles, weather/places/site/market adapters, cache, and a daily cost governor. |
| mcp-server | the ASP | 13 tools behind the x402 gate, durable jobs and idempotency, the store, anchor worker, A2A negotiation, public endpoints, and the internal demo SSE that drives the Studio. |
| client | sdk | @occestra/client — a tiny typed SDK with the payment flow built in. |
| apps/web | next 15 | This site: landing, Studio, /k verify pages, gallery, standard, stats, docs. Self-hosted standalone behind Caddy. |
Five decisions that carry the weight
- 1. The Tribunal is injected, not imported. studio-core cannot depend on tribunal (cycle, and purity), so pipelines ask for grades through a
GradePortand the server hands them the real engine — repair loop and all. Tests hand them fakes. Same pipeline, both worlds. - 2. Deterministic checks outrank the model. A hard failure cannot be argued away by a high score. This ordering is why our own marketing copy failed its own grading — and why that story is credible.
- 3. Degrade honestly, never silently. Every provider call can fail without aborting a pack; the failure becomes a recorded coverage gap. During a real OpenAI billing outage the pipelines kept delivering — smaller, disclosed, true.
- 4. No facilitator in payments. The gate verifies EIP-3009 signatures itself and settles with its own gas key. Fewer moving parts, no third party to trust, and refunds-by-construction: a failed settlement releases the nonce.
- 5. Hash-only chain. The contract stores 32-byte leaves and timestamps. Nothing personal, nothing revocable, nothing to leak — and verification works from public data alone.
Operations
| field | type | notes |
|---|---|---|
| hosting | self-hosted | Plain Node 22 + systemd + Caddy (auto-HTTPS) on a VPS. No Docker, no platform lock-in. |
| services | 2 units | occestra-mcp (the ASP) and occestra-web (Next standalone), isolated ports, shared secrets via env files (0600). |
| reliability | boring on purpose | Health checks every 5 min with auto-restart, nightly backups (14-day retention), log rotation, anchor worker with retry + backoff that never takes the server down. |
| budget | governed | Daily image and LLM-USD caps enforced by the cost governor; the Studio demo is separately metered and its runs are recorded as status 'demo', never as revenue. |
Design system — reflected by day, luminous by night
The product has one editorial system and two light conditions. Amethyst Daylight uses warm ivory paper, ink typography and restrained reflected amethyst. Amethyst Nocturne keeps the same hierarchy on an aubergine ground, brightens grade colors to WCAG AA, and lets seals, active states and primary actions emit a soft bloom. The setting defaults to the operating-system preference, is applied before first paint, and persists locally.
Texture is code, not downloadable decoration: a fixed inline-SVG turbulence layer reads as paper grain (multiply in Daylight, screen in Nocturne); parametric guilloché rosettes and certificate borders mark provenance moments; warm radial vignettes give section heads depth without sitting behind body copy. All motion collapses underprefers-reduced-motion, while Open Graph images remain theme-stable so a shared pack never changes identity with the viewer's preference.
There is deliberately no WebGL hero object. The visual budget belongs to the product flow: route sheets arrive, sections reveal in editorial sequence, the real Studio feed follows work between roles, failed artifacts return for repair, finished artifacts settle into the pack, and the seal press lands with weight. Removing the decorative scene also removes a separate loading, GPU and fallback state from the first impression.
Motion explains state: grades count up once, a failed artifact visibly returns for repair, active roles pulse, finished cards settle, and the seal's guilloché turns during the press. The recent-seals ribbon is populated from anonymized public store data, never invented activity. Sound follows the same restraint: persisted, default off, and never fetched at all until you ask for it — one opt-in gates both a soft seal foley and a quiet looping room tone, and a remembered preference still waits for a real gesture rather than starting on its own.
AGENTS.md's deviations log — the repo carries its own honest history.