# Roles domain

The `roles` domain describes who does what inside the organization — a role chart inspired by the
EOS Accountability Chart but using plain-language field names throughout. Agents use this domain
for HITL routing, escalation logic, and approval-rights reasoning.

Field names are developer-facing; when narrating to users, say "role", "responsibilities",
"reports to", and "held by". Do not say "seat", "accountability", or "EOS".

## Schema

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

```typescript
RolesDomainSchema = z.object({
  roles: z.array(RoleSchema).default([])
})

RoleSchema = z.object({
  id:               z.string().trim().min(1).max(100),              // stable ID, e.g. "role-ceo"
  title:            z.string().trim().min(1).max(200),              // display title, e.g. "CEO"
  responsibilities: z.array(z.string().trim().max(500)).default([]),// what this role owns
  reportsToId:      z.string().trim().min(1).max(100).optional(),   // must ref another roles[].id
  heldBy:           z.string().trim().max(200).optional()           // name or email, free-form
})
```

## Field reference

| Field              | Type     | Max | Notes                                                           |
| ------------------ | -------- | --- | --------------------------------------------------------------- |
| `id`               | string   | 100 | Stable key, min 1 char; e.g. `"role-ceo"`                       |
| `title`            | string   | 200 | Display title; min 1 char required                              |
| `responsibilities` | string[] | 500 | Each item: one thing this role is accountable for               |
| `reportsToId`      | string   | 100 | Optional; must reference another `roles[].id` in the collection |
| `heldBy`           | string   | 200 | Optional; name or email of the person holding this role         |

## Validation rules

- `reportsToId` (when present) must resolve to another `roles[].id` in the same collection;
  enforced at parse time by `OrganizationModelSchema.superRefine()`
- Absence of `reportsToId` indicates a top-level role (no reporting line)
- Cycle detection is not enforced by the schema (known limitation); agents should surface
  obvious cycles (e.g. role A reports to role B which reports to role A)
- `heldBy` is free-form; no validation beyond max length
- An empty roles array means no HITL routing or escalation rules are available

## Examples

```typescript
roles: {
  roles: [
    {
      id:               "role-ceo",
      title:            "CEO",
      responsibilities: [
        "Set company direction",
        "Final approval on deals >$10K",
        "Manage investor relations"
      ],
      reportsToId: undefined,
      heldBy:      "Alice Johnson"
    },
    {
      id:               "role-head-of-client-success",
      title:            "Head of Client Success",
      responsibilities: [
        "Own client health scores",
        "Lead quarterly business reviews",
        "Handle escalations"
      ],
      reportsToId: "role-ceo",
      heldBy:      undefined
    }
  ]
}
```

## Where it lives in the adapter

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

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

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

## Write path

To add, edit, or remove a role, 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 `roles` array is
written; no other keys in the target file are touched. Before writing, the skill verifies all
`reportsToId` references resolve to IDs that will exist after the write. When removing a role,
the skill surfaces any other roles that `reportsToId` to it and asks whether to clear those
reporting lines before proceeding.

---

**Read via `/om`** (legacy `/knowledge` alias also accepted) — natural-language queries like
"who is responsible for client escalations?" or "list my roles" route to this domain via the
skill's intent classifier.
