Architectural overview of Annapurna, the restaurant / culinary-operations / autonomous-delivery domain: the implemented
@annapurna/*TypeScript library workspace, its sharedcorefoundation, the per-pillar engine packages, the cross-domain contract and integration layers, and an honest map of what is built versus what is still planned.
Annapurna is the Oshun domain for commercial food-service operations — menu engineering, culinary intelligence, kitchen automation, front- and back-of-house workflow, procurement and food safety, autonomous and human delivery, ghost kitchens, multi-unit chains, commissaries, workforce, restaurant finance, customer intelligence, and sustainability. It is the operating system for a business that sells food, deliberately distinct from Hestia, which owns home cooking; the two meet at the point of sale.
The domain takes its name from Annapurna (अन्नपूर्णा), the Hindu goddess of food and complete nourishment — chosen because the platform aims to close the whole commercial food loop, from a supplier lot through a robot-assembled dish to a guest at a table or a thermally-controlled delivery handoff.
Status correction (important). The sibling features.md and
specifications.md pages still describe Annapurna as "planned-only, no packages
exist." That is no longer true and those pages are stale. The workspace is
implemented: libs/annapurna/domain.config.json records
"status": "foundation-and-core-implemented", and the tree below contains
twenty-one @annapurna/* libraries plus a @contracts/annapurna package, all
real TypeScript with tests. Where the older pages and the shipped code disagree
on specifics (order statuses, MenuItem shape, an IngredientLot lifecycle),
this page follows the code and flags the divergence.
A new engineer should hold three facts in mind:
- Annapurna is a pure-TypeScript domain library workspace, not a running
service. Every package exports deterministic, side-effect-free domain
functions over shared types. There is no
apps/annapurna/orservices/annapurna/directory; there is no deployed runtime yet. - One package —
@annapurna/core— owns every shared type, ID, Zod schema, the order state machine, the persistence schema, and the wire-contract surface. Every other package depends on it and (almost) nothing else. - The polyglot stack the specification imagines (Rust robotics control, ROS2 navigation, Python ML, C/C++ firmware, live Kafka/WebSocket, React Native apps) is not present. What exists is the deterministic decision logic those runtimes would eventually wrap.
Workspace Shape#
libs/annapurna/
├── domain.config.json ← { phase: 68, scopeTag: "scope:annapurna",
│ status: "foundation-and-core-implemented" }
├── core/ ← @annapurna/core — shared foundation
│ ├── src/ids.ts ← branded IDs + prefix-validated factories
│ ├── src/types.ts ← entities, enums, COOKING_TECHNIQUES, order FSM
│ ├── src/schemas.ts ← Zod schemas mirroring every entity
│ ├── src/taxonomy.ts ← ANNAPURNA_CUISINE_TAXONOMY (250+ cuisines)
│ ├── src/db-schema.ts ← table/index metadata + Redis cache strategy
│ ├── src/api.ts ← REST/GraphQL/gRPC/WS/Kafka/OpenAPI surface
│ └── drizzle/0001_annapurna_foundation.sql ← real DDL migration
│
├── culinary/ menu/ ← Culinary Intelligence pillar
├── restaurant/ kitchen/ foh/ boh/ ← Design & Operations pillar
├── robotics/ ai/ ← Automation & Robotics pillar
├── delivery/ ← Autonomous Delivery pillar
├── chain/ ghost/ commissary/ ← Multi-Unit Intelligence pillar
├── supply/ safety/ ← Supply Chain & Food Safety pillar
├── staff/ finance/ customer/ sustainability/ ← cross-cutting operations
├── apps/ ← view-model state builders for end-user apps
└── integration/ ← cross-domain adapter layer
libs/contracts/annapurna/ ← @contracts/annapurna — wire contracts
Every feature package follows an identical skeleton: an index.ts that exports
a self-describing capability registry (ANNAPURNA_<PKG>_CAPABILITIES plus
list…Capabilities() / has…Capability()), a types.ts of domain models, an
engines.ts of pure functions, and a colocated Vitest suite. The capability
registry is the package's machine-readable manifest of what it claims to do and
which source files back each claim.
Design Principles#
The five rules below are how the workspace is actually built, observed across all twenty-one packages.
- One foundation, many leaves.
@annapurna/coreis the single source of truth for entity types, branded IDs, enums, and the order state machine. Feature packages import from@annapurna/coreand generally not from each other — they are independently buildable leaves over a shared trunk.@annapurna/integrationis the only package that fans out, and it fans out to other domains, not to Annapurna siblings. - Pure functions, no hidden state. Engines take inputs and return results.
There is no database client, no network call, no clock dependency, no global
mutable state inside the domain logic. Persistence and transport are
described as data (
db-schema.ts,api.ts) but not wired. - Branded identifiers everywhere. Entity references are nominal brands
(
Brand<string, 'OrderId'>) minted only through prefix-validating factories (createOrderIdrequires anord_prefix), so aMenuItemIdcan never be passed where anOrderIdis expected. - Schema and type parity. Every entity in
types.tshas a matching Zod schema inschemas.ts. Validation at the boundary and compile-time typing inside the domain stay in lockstep. - Domain-specific math, not CRUD. The engines encode real food-service formulas — non-linear recipe scaling, the menu-engineering matrix, HACCP critical limits, FEFO inventory, prime-cost and contribution-margin accounting — rather than generic record shuffling.
Component & Data Flow#
Control flows top-down: a hypothetical orchestrating service (not yet built)
would validate input with a @contracts/annapurna Zod schema, call the relevant
engine functions, advance an Order through the core state machine, and emit a
@contracts/annapurna domain event. Data flows through the shared core
entities the whole way.
The Foundation: @annapurna/core#
core is the package every architectural decision routes through.
Branded IDs (libs/annapurna/core/src/ids.ts). Ten entity ID brands —
RestaurantId, MenuItemId, OrderId, VehicleId, TableId, StaffId,
RecipeId, IngredientId, SupplierId, CustomerId — each minted by a
factory that trims input and enforces a prefix (rest_, menu_, ord_,
veh_, tbl_, stf_, rcp_, ing_, sup_, cus_). An empty or
wrong-prefix value throws.
Entities and enums (libs/annapurna/core/src/types.ts, ~1,000 lines).
Restaurant, KitchenStation, MenuItem, Order, DeliveryVehicle, Table,
Reservation, StaffMember, Recipe, Ingredient, FlavorProfile,
CuisineType, DietaryRequirement, plus value objects (Money over a fixed
currency set including GHS/USD/EUR/NGN/KES/ZAR, NutritionalData,
ThermalCompartment, NavigationCapability). Enums fix the operational
vocabulary: RestaurantConcept (fine dining → ghost kitchen → food truck),
KitchenStationName (grill, saute, fry, pastry, wok, sushi…), StaffRole,
VehicleClass (sidewalk robot, road robot, delivery drone, bicycle…),
OrderChannel, and OrderFulfillmentStatus. The file also defines
COOKING_TECHNIQUES — a large catalog generated by crossing a base technique
list (grill, sous*vide, ferment, spherify…) with heat-method prefixes
(classic*, low*temp*, high*heat*, pressure\_), yielding the
several-hundred technique union the recipe model references.
Order state machine (same file). ORDER_STATUS_TRANSITIONS is the single
authority for order lifecycle:
placed → confirmed → preparing → ready → picked_up → delivered, with
cancelled reachable from placed/confirmed/ preparing/ready but not
from picked_up or delivered (those are terminal).
transitionOrder(order, next, atIso) throws on any illegal edge and stamps the
new status timestamp into OrderTimestamps; canTransitionOrder is the pure
predicate behind it. This is the one runtime invariant the code actually
enforces.
Divergence note: the stale
specifications.mddescribes a different machine (created → … → served) and channels (takeaway,catering,ghost_kitchen). The shippedOrderChannelisdine_in | takeout | delivery | platformand the status set is the seven values above. It also describes anIngredientLot.traceabilityStatuslifecycle and aMenuItem.grossMarginPercentfield that do not exist incore; the realMenuItemcarriespriceTiers: Record<OrderChannel, Money>,dietaryTags,nutrition, and arecipeId, and lot traceability is modelled in the safety/supply packages and theannapurna_inventory_lotstable rather than as a core enum.
Schemas (libs/annapurna/core/src/schemas.ts). A Zod schema for every
entity, including ID schemas that re-assert the prefix rule, MoneySchema,
NutritionalDataSchema, FlavorProfileSchema (each taste axis clamped to
[0,1]), and OrderSchema with a per-status timestamp object. These are the
validation boundary for any service that later wraps the domain.
Cuisine taxonomy (libs/annapurna/core/src/taxonomy.ts, ~6,500 lines).
ANNAPURNA_CUISINE_TAXONOMY is a large structured catalog of CuisineType
records (250+ regional cuisines spanning Africa, Europe, Asia, the Americas),
each carrying signature dishes, core techniques, staple ingredients, and a
FlavorProfile. It is the reference data the culinary engine draws on for
concept generation and flavor reasoning.
Persistence description (libs/annapurna/core/src/db-schema.ts +
drizzle/0001_annapurna_foundation.sql). Twelve tables —
annapurna_restaurants, annapurna_menus, annapurna_recipes,
annapurna_orders, annapurna_inventory_lots, annapurna_kitchen_equipment,
annapurna_delivery_vehicles, annapurna_staff, annapurna_customers,
annapurna_suppliers, and two TimescaleDB hypertables
(annapurna_kitchen_iot_telemetry, annapurna_delivery_vehicle_telemetry). The
real SQL migration enables postgis, timescaledb, and vector; restaurants
and vehicles carry geography(point,4326) columns with GiST indexes; recipes
and customers carry vector(1536) embedding columns with IVFFlat cosine
indexes. ANNAPURNA_REDIS_CACHE_STRATEGY defines TTL'd key patterns for order
state, KDS display, delivery tracking, and menu availability. This is a faithful
schema; it is metadata and DDL, not a running database or repository layer.
Wire surface (libs/annapurna/core/src/api.ts). REST endpoints with
operation IDs, a GraphQL SDL fragment, WebSocket channel names
(annapurna.kds.…, annapurna.customer.order_status,
annapurna.delivery.vehicle_position), a gRPC proto (KitchenRobotics,
DeliveryDispatch, InventoryUpdates), Kafka topic names, a generated OpenAPI
3.1 document, Prometheus metric names, trace attributes, and auth roles.
@contracts/annapurna (libs/contracts/annapurna/src/events.ts and siblings)
re-states the cross-domain contract — notably an AnnapurnaDomainEventSchema
with eight typed event types (annapurna.order.placed, annapurna.food.ready,
annapurna.delivery.delivered, annapurna.inventory.low, …) keyed by aggregate
type. Again: a described surface, not a mounted server.
The Pillar Engines#
Each engine package turns the shared core types into food-service decisions.
Highlights, by pillar:
Culinary Intelligence — culinary, menu. culinary
(libs/annapurna/culinary/src/engines.ts, ~1,300 lines) is the deepest engine:
scaleRecipe applies non-linear scaling exponents by ingredient class
(seasoning 0.82, leavening 0.70, garnish 0.90, sauces 0.95) rather than scaling
everything linearly; predictFlavorPairings scores ingredient pairs on shared
aroma chemistry; optimizeMaillardReaction, planFermentation, and
planMolecularTechnique emit technique-specific parameter envelopes;
buildAllergenMatrix propagates allergens through ingredient → recipe →
menu-item and tags EU-14 and FDA-Big-9 sets; calculateRecipeNutrition rolls up
per-portion nutrition with cooking-loss adjustments. menu implements the
menu-engineering matrix (classifyMenuEngineering → star / plowhorse /
puzzle / dog by popularity × contribution margin), psychological and anchor
pricing (recommendMenuPrice), demand/daypart calculateDynamicPrice, and
trackFoodCostVariance (theoretical vs. actual food cost).
Design & Operations — restaurant, foh, plus kitchen/boh.
restaurant (libs/annapurna/restaurant/src/engines.ts, ~710 lines) covers
both front-of-house spatial design (concept, dining layout, biophilic, lighting,
acoustics, bar, outdoor) and the full commercial-kitchen design suite
(planCommercialKitchenLayout, specifyKitchenEquipment,
designKitchenVentilation, specifyRefrigeration, specifyPlumbing,
planElectricalLoad, scheduleEquipmentMaintenance). foh runs POS sessions,
KDS ticket routing, multi-channel order aggregation, kitchen-capacity
throttling, reservations, waitlist, table-turn optimization, loyalty, and
feedback.
Automation — robotics, ai. robotics
(libs/annapurna/robotics/src/engines.ts, ~690 lines) compiles dishes into
station controller programs (wok, grill/plancha, fryer, pizza line, sushi,
salad, beverage, pastry) as timed action + motion-profile sequences,
synchronizes a multi-robot table to a single ready time via bottleneck analysis,
plans sanitization cycles against ATP-RLU thresholds, enforces dispense
tolerances (±1 g spices, ±5 g proteins), and runs IoT/energy/air-quality
monitoring plus plate-QC and cooking-completion vision fusion. ai
(libs/annapurna/ai/src/engines.ts, ~870 lines) provides deterministic
heuristic models — demand forecasting, plate-quality scoring, customer
recommendations, dynamic-price optimization, kitchen-efficiency, review
sentiment, route optimization, trend prediction. These are CPU heuristics in
TypeScript, not trained ML models.
Supply Chain & Food Safety — supply, safety. supply implements
supplier sourcing, par-level purchasing, receiving inspection, perpetual
inventory, enforceFefo (first-expiry-first-out), shelf-life management, and
food-cost accounting. safety (libs/annapurna/safety/src/engines.ts) builds
HACCP plans grounded in Codex Alimentarius / FDA Food Code, identifying critical
control points with explicit critical limits (e.g. cool 57 °C → 21 °C within 2
h, → 5 °C within 6 h; hot-hold ≥ 57 °C), temperature-deviation alerting,
inspection- readiness scoring, allergen-management protocols, ATP-validated
cleaning, and buildRecallResponsePlan, which resolves a recall notice to
affected lots and the exact servedOrderIds — the one place lot→order
traceability is concretely realized.
Multi-Unit, Finance, Customer, Sustainability. chain, ghost, and
commissary handle franchise/portfolio, virtual-brand, and central-kitchen
operations; finance produces P&L, prime-cost, break-even, cash-flow, and
delivery-channel profitability; customer runs RFM segmentation, CLV, and churn
prediction; staff does demand-based scheduling and labor analytics;
sustainability tracks waste, carbon, sourcing, energy, and water.
Surface — apps, integration. apps builds view-model state objects
(customer mobile state, order-tracking, KDS tablet, fleet dashboard, courier
app, robot teleoperation) — the data an app would render, not the React Native
app itself. integration (libs/annapurna/integration/src/engines.ts) is the
cross-domain adapter layer described below.
Cross-Domain Integration#
@annapurna/integration is the only Annapurna package that reaches outside the
domain, and it does so through structural adapters, not live calls. Each
adapter (adaptHestiaRecipeIntelligence,
buildAsaseFarmToRestaurantSupplyChain, adaptBrigidKitchenAutomation,
adaptOyaAerialDelivery, adaptSeshatRestaurantDesign,
adaptMaatBusinessOperations, adaptPsycheCustomerInteraction, and adapters
for Athena furniture and Freya textiles) takes a typed source payload and
returns an AdapterIntegrationResult naming the source/target packages, the
handoff artifacts, the risk controls, and a status of blocked / ready /
needs_review. This encodes the ownership boundaries the domain documents
promise:
- Hestia owns home cooking; Annapurna imports recipe/nutrition intelligence and exports meal-kit SKUs back. The boundary is the point of sale.
- Asase owns farm-level traceability; Annapurna picks the chain up at the
receiving dock (
@annapurna/supply/@annapurna/safety). - Brigid owns industrial equipment, refrigeration, and energy; Annapurna adds the food-service-specific control and safety layer.
- Oya owns aerial/ground autonomous vehicles; Annapurna consumes flight and fleet capability for delivery.
- Maat consolidates cross-domain finance, compliance, and workforce;
@annapurna/financeand@annapurna/stafffeed it. - Seshat / Athena / Freya / Psyche supply spatial harmony, furniture, textiles, and conversational/voice surfaces.
Because the adapters are pure functions returning a status, they are the natural
seam where a future service would attach real transport — and they fail honestly
(returning blocked/needs_review) rather than pretending an integration is
live.
Invariants, Failure Modes, and Extension Points#
- Enforced invariant: illegal order transitions throw in
transitionOrder. This is the only hard runtime guard in the domain today. - Encoded-but-unenforced intentions: the four "platform hard requirements"
the older docs list (immutable safety records, lot-to-order traceability,
versioned menu pricing, robotic safety-stop / incident log) are partially
represented — recall traceability exists in
safety, the inventory-lot table exists indb-schema— but there is no persistence layer enforcing immutability or append-only logs, and no robotic safety-stop state machine inrobotics. Safety interlocks appear only as gRPC fields and integration risk controls. Treat these as design targets, not guarantees. - Failure mode to watch: because engines are stateless, two packages can
compute inconsistent views if a caller feeds them divergent snapshots. The
intended mitigation is a single orchestrating service reading from the
coreschema — which does not exist yet. - Thin packages:
@annapurna/kitchenand@annapurna/bohare capability registries only — they export the manifest interface andlist/hashelpers but no engine logic. Kitchen-design behavior actually lives in@annapurna/restaurant; back-of-house behavior is distributed acrossculinary(prep/batch),robotics(cooking/cleaning), andsupply(inventory). Do not look for substantive code in those two packages. - Extension points: add a new capability by extending an engine and
appending to its
ANNAPURNA_<PKG>_CAPABILITIESregistry; add a new entity by defining it incore/src/types.ts, mirroring the Zod schema inschemas.ts, and (if persisted) adding a table todb-schema.tsand the Drizzle migration; add a new external dependency by writing anAdapterIntegrationResult-returning function inintegration.
Honest Status Summary#
Implemented and tested (TypeScript, pure-function): @annapurna/core (IDs,
entities, enums, cooking-technique catalog, order state machine, Zod schemas,
cuisine taxonomy, DB schema metadata + real Drizzle SQL, wire-contract surface);
@contracts/annapurna; and eighteen feature engines — culinary, menu,
restaurant, foh, robotics, ai, supply, safety, staff, finance,
customer, chain, ghost, commissary, sustainability, delivery,
apps, integration — each with substantive, domain-specific logic and
colocated tests.
Thin / placeholder: @annapurna/kitchen and @annapurna/boh (capability
manifests only).
Described but not wired: persistence (no running Postgres/Timescale/pgvector or repository layer behind the schema), Redis caching, Kafka/WebSocket transport, the REST/GraphQL/gRPC servers (only the contract surface exists), and the enforced platform invariants.
Planned, not present: the polyglot runtime — Rust kitchen-robotics and
delivery-navigation control, ROS2 autonomy, Python ML models, C/C++ embedded
firmware — and shippable React Native applications (the apps package builds
view-model state, not the apps). There are no apps/annapurna/ or
services/annapurna/ projects.
In short: Annapurna today is a complete, code-grounded domain-logic and contract layer for commercial food service — the deterministic brain a future set of services and device runtimes would orchestrate — and it should be read as exactly that, not as a deployed platform.