# Annapurna Domain — Architecture

> Architectural overview of Annapurna, the restaurant / culinary-operations /
> autonomous-delivery domain: the implemented `@annapurna/*` TypeScript library
> workspace, its shared `core` foundation, 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:

1. 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/` or
   `services/annapurna/` directory; there is no deployed runtime yet.
2. 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.
3. 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.

1. **One foundation, many leaves.** `@annapurna/core` is the single source of
   truth for entity types, branded IDs, enums, and the order state machine.
   Feature packages import from `@annapurna/core` and generally not from each
   other — they are independently buildable leaves over a shared trunk.
   `@annapurna/integration` is the only package that fans out, and it fans out
   to _other domains_, not to Annapurna siblings.
2. **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.
3. **Branded identifiers everywhere.** Entity references are nominal brands
   (`Brand<string, 'OrderId'>`) minted only through prefix-validating factories
   (`createOrderId` requires an `ord_` prefix), so a `MenuItemId` can never be
   passed where an `OrderId` is expected.
4. **Schema and type parity.** Every entity in `types.ts` has a matching Zod
   schema in `schemas.ts`. Validation at the boundary and compile-time typing
   inside the domain stay in lockstep.
5. **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

```mermaid
flowchart TB
  subgraph Foundation
    CORE["@annapurna/core\nids · types · schemas · taxonomy\norder state machine · db-schema · api"]
    CONTRACTS["@contracts/annapurna\nevents · api-schemas · graphql\ngrpc · openapi · integration"]
  end

  subgraph Culinary["Culinary Intelligence"]
    CUL["@annapurna/culinary"]
    MENU["@annapurna/menu"]
  end
  subgraph Ops["Design & Operations"]
    REST["@annapurna/restaurant\n(+ kitchen design)"]
    FOH["@annapurna/foh"]
    SUP["@annapurna/supply"]
    STAFF["@annapurna/staff"]
    FIN["@annapurna/finance"]
    CUST["@annapurna/customer"]
    CHAIN["@annapurna/chain"]
    GHOST["@annapurna/ghost"]
    COMM["@annapurna/commissary"]
    KIT["@annapurna/kitchen*"]
    BOH["@annapurna/boh*"]
  end
  subgraph Automation
    ROB["@annapurna/robotics"]
    AI["@annapurna/ai"]
  end
  subgraph SafetyDelivery
    SAFE["@annapurna/safety"]
    DEL["@annapurna/delivery"]
    SUS["@annapurna/sustainability"]
  end
  subgraph Surface
    APPS["@annapurna/apps\n(view-model state)"]
    INT["@annapurna/integration\n(adapters)"]
  end

  CUL & MENU & REST & FOH & SUP & STAFF & FIN & CUST --> CORE
  CHAIN & GHOST & COMM & ROB & AI & SAFE & DEL & SUS & APPS & INT --> CORE
  KIT & BOH --> CORE
  CONTRACTS -. mirrors .-> CORE

  INT --> HESTIA["@hestia/*"]
  INT --> ASASE["@asase/*"]
  INT --> BRIGID["@brigid/*"]
  INT --> OYA["@oya/*"]
  INT --> MAAT["@maat/*"]
  INT --> SESHAT["@seshat/* · @athena/* · @freya/* · @psyche/*"]

  CORE -. described, not wired .-> PG[("Postgres\nPostGIS · pgvector · TimescaleDB")]
  CORE -. described, not wired .-> REDIS[("Redis cache")]
  CORE -. described, not wired .-> KAFKA{{"Kafka topics"}}

  %% * kitchen/boh are thin capability registries; kitchen design lives in restaurant
```

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.md` describes a different machine
> (`created → … → served`) and channels (`takeaway`, `catering`,
> `ghost_kitchen`). The shipped `OrderChannel` is
> `dine_in | takeout | delivery | platform` and the status set is the seven
> values above. It also describes an `IngredientLot.traceabilityStatus`
> lifecycle and a `MenuItem.grossMarginPercent` field that **do not exist** in
> `core`; the real `MenuItem` carries `priceTiers: Record<OrderChannel, Money>`,
> `dietaryTags`, `nutrition`, and a `recipeId`, and lot traceability is modelled
> in the safety/supply packages and the `annapurna_inventory_lots` table 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/finance` and `@annapurna/staff` feed 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 in `db-schema` — but there is **no persistence layer enforcing
  immutability or append-only logs, and no robotic safety-stop state machine**
  in `robotics`. 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 `core`
  schema — which does not exist yet.
- **Thin packages:** `@annapurna/kitchen` and `@annapurna/boh` are **capability
  registries only** — they export the manifest interface and `list/has` helpers
  but no engine logic. Kitchen-design behavior actually lives in
  `@annapurna/restaurant`; back-of-house behavior is distributed across
  `culinary` (prep/batch), `robotics` (cooking/cleaning), and `supply`
  (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>_CAPABILITIES` registry; add a new entity by
  defining it in `core/src/types.ts`, mirroring the Zod schema in `schemas.ts`,
  and (if persisted) adding a table to `db-schema.ts` and the Drizzle migration;
  add a new external dependency by writing an
  `AdapterIntegrationResult`-returning function in `integration`.

---

## 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.
