# Offerings domain

The `offerings` domain describes what the organization sells or delivers — a catalog of products
and services modeled after the Business Model Canvas "Value Propositions". Agents use offerings
for quoting, proposal generation, and scope-of-work reasoning.

## Schema

Source: `packages/core/src/organization-model/domains/offerings.ts`

```typescript
OfferingsDomainSchema = z.object({
  products: z.array(ProductSchema).default([])
})

ProductSchema = z.object({
  id:                z.string().trim().min(1).max(100),       // stable ID, e.g. "product-starter-plan"
  name:              z.string().trim().max(200).default(''),  // display name
  description:       z.string().trim().max(2000).default(''), // what it delivers
  pricingModel:      PricingModelSchema.default('custom'),    // 'one-time' | 'subscription' | 'usage-based' | 'custom'
  price:             z.number().min(0).default(0),            // base price amount (>= 0)
  currency:          z.string().trim().max(10).default('USD'),// ISO 4217, e.g. "USD", "EUR"
  targetSegmentIds:  z.array(z.string()).default([]),         // must ref customers.segments[].id
  deliverySystemId: z.string().optional()                     // optional ref to systems[].id
})
```

`pricingModel` values:

- `one-time` -- single purchase (setup fee, project fee)
- `subscription` -- recurring (monthly/annual SaaS, retainer)
- `usage-based` -- metered by consumption (API calls, seats)
- `custom` -- negotiated or bespoke pricing

## Field reference

| Field              | Type     | Max  | Notes                                                    |
| ------------------ | -------- | ---- | -------------------------------------------------------- |
| `id`               | string   | 100  | Stable key, min 1 char; e.g. `"product-starter-plan"`    |
| `name`             | string   | 200  | Display name                                             |
| `description`      | string   | 2000 | What this product or service delivers                    |
| `pricingModel`     | enum     | --   | `one-time`, `subscription`, `usage-based`, or `custom`   |
| `price`            | number   | --   | Base price amount, min 0; use 0 for fully custom pricing |
| `currency`         | string   | 10   | ISO 4217 code; defaults to `"USD"`                       |
| `targetSegmentIds` | string[] | --   | Each entry must reference a `customers.segments[].id`    |
| `deliverySystemId` | string   | --   | Optional reference to a `systems[].id`                   |

## Validation rules

- `id` is the stable key; changing it after creation is a breaking change for any downstream
  references
- `targetSegmentIds` entries must exist in `customers.segments[].id`; the schema enforces this
  at parse time — missing references cause `resolveOrganizationModel()` to fail
- `deliverySystemId` (when present) must exist in `systems[].id`; same parse-time enforcement
- `price` must be >= 0; use 0 when pricing is fully custom or not yet set
- `currency` must be a valid ISO 4217 code by convention (the schema only validates length)
- Cross-reference: if a product is removed, surface any `targetSegmentIds` in other products
  that reference the removed ID

## Examples

```typescript
offerings: {
  products: [
    {
      id:               "product-full-service-retainer",
      name:             "Full-Service Retainer",
      description:      "End-to-end campaign delivery, reporting, and optimization for SMB clients.",
      pricingModel:     "subscription",
      price:            2500,
      currency:         "USD",
      targetSegmentIds: ["segment-smb-agencies"],
      deliverySystemId: undefined
    },
    {
      id:               "product-one-time-audit",
      name:             "One-Time Operations Audit",
      description:      "Single-engagement review of current workflows with a remediation roadmap.",
      pricingModel:     "one-time",
      price:            1500,
      currency:         "USD",
      targetSegmentIds: ["segment-smb-agencies"],
      deliverySystemId: undefined
    }
  ]
}
```

## Where it lives in the adapter

**Split projects:** `core/config/organization-model/profile.ts` under the `offerings` key of
`defineOrganizationModel({...})`.

**Unsplit projects:** `core/config/organization-model.ts` under the same key.

To read current products: open the appropriate file and inspect the `offerings.products` array,
or use `pnpm exec elevasis-sdk om:cat offerings` (external project).

## Write path

To add, edit, or remove a product, the `/om` skill runs the codify ceremony
(`operations/codify-level-a.md`): snapshot → propose → confirm → write → typecheck → Zod parse →
rollback on failure. Pass `targetFile` as `core/config/organization-model/profile.ts` for split
projects, or `core/config/organization-model.ts` for unsplit projects. Only the `products` array
is written; no other keys in the target file are touched. Before writing, the skill verifies all
`targetSegmentIds` and `deliverySystemId` references resolve. When adding, append to the array.
When editing, replace by `id`. When removing, filter out — and surface any dangling references
first.

---

**Read via `/om`** (legacy `/knowledge` alias also accepted) — natural-language queries like
"what do we sell?" or "what's the pricing on the retainer?" route to this domain via the skill's
intent classifier.
