# @x12i/memorix-descriptors

> Manage Memorix entity, list, and item descriptors in Catalox

Latest version **1.38.0** (published 2026-07-21) · 0 weekly downloads

## Install

```sh
npm install @x12i/memorix-descriptors
pnpm add @x12i/memorix-descriptors
yarn add @x12i/memorix-descriptors
bun add @x12i/memorix-descriptors
```

Provides the command `memorix-descriptors`.

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 1.38.0 |
| Published | 2026-07-21 |
| First published | 2026-05-23 |
| Weekly downloads | 0 |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=18.0.0 |
| Dependencies | 4 |
| Unpacked size | 1.5 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | x12i |

## Links

- npm: https://www.npmjs.com/package/@x12i/memorix-descriptors
- Repository: https://github.com/x12i/memorix-mono-repo
- Homepage: https://github.com/x12i/memorix-mono-repo#readme
- Issues: https://github.com/x12i/memorix-mono-repo/issues
- npm.io page: https://npm.io/package/@x12i/memorix-descriptors

## Dependencies (4)

- [mongodb](https://npm.io/package/mongodb.md) ^6.21.0
- [@x12i/catalox](https://npm.io/package/@x12i/catalox.md) ^5.10.1
- [@x12i/helpers](https://npm.io/package/@x12i/helpers.md) ^2.1.0
- [@x12i/memorix-db-router](https://npm.io/package/@x12i/memorix-db-router.md) ^1.0.0

## Recent versions

- 1.38.0 (latest) — 2026-07-21
- 1.37.1 — 2026-07-19
- 1.37.0 — 2026-07-19
- 1.36.0 — 2026-07-13
- 1.35.0 — 2026-07-13
- 1.34.0 — 2026-07-13
- 1.33.0 — 2026-07-13
- 1.32.0 — 2026-07-11
- 1.23.0 — 2026-07-07
- 1.22.0 — 2026-07-07
- 1.21.0 — 2026-07-05
- 1.20.0 — 2026-07-04
- 1.19.0 — 2026-07-04
- 1.18.0 — 2026-07-02
- 1.17.0 — 2026-06-30
- … 21 more at https://npm.io/package/@x12i/memorix-descriptors/versions

## README

# @x12i/memorix-descriptors

Write-side control plane for Memorix entity, list, and item descriptors — plus content types, collections, relations, and completion mappings — stored in Catalox and consumed by `@x12i/memorix-retrieval`.

## Role in the ecosystem

```
Hosts (Explorer Admin, CI, CLI, internal tools)
        │
        ▼
@x12i/memorix-descriptors   ← validate, mutate, seed, reconcile (this package)
        │
        ├─ write ──► @x12i/catalox (descriptor catalogs)
        │
        └─ read / inventory ──► @x12i/memorix-retrieval (+ optional @x12i/xronox)
```

| Concern | Package |
|--------|---------|
| Load descriptors, compose lists/items, graph, runtime reads | `@x12i/memorix-retrieval` |
| Create/update/delete descriptors and mappings in Catalox | `@x12i/memorix-descriptors` |
| Write Memorix payload documents to Mongo | `@x12i/memorix-completion` |

Canonical contracts: [`docs/MEMORIX-CATALOX-CONTRACTS.md`](docs/MEMORIX-CATALOX-CONTRACTS.md), [`docs/MEMORIX-DATABASE-CONVENTIONS.md`](docs/MEMORIX-DATABASE-CONVENTIONS.md). **Object types vs storage targets** (naming cleanup): [`docs/MEMORIX-OBJECT-TYPES-AND-TARGETS.md`](docs/MEMORIX-OBJECT-TYPES-AND-TARGETS.md).

---

## Install

```bash
npm install @x12i/memorix-descriptors @x12i/catalox
# Optional, for Mongo inventory / reconcile:
npm install @x12i/xronox
```

Optional peer: `@x12i/xronox` ≥3.9 for Mongo inventory / reconcile. This package does **not** depend on `@x12i/memorix-retrieval`.

---

## Package exports

The main entry re-exports the full write-side surface (mutations, seeds, reconcile, CLI helpers).

**Retrieval-safe subpaths** — for `@x12i/memorix-retrieval` and other read-side consumers that must not import the main `"."` entry (avoids TS5055 cycles):

| Subpath | Symbols | Use |
|---------|---------|-----|
| `@x12i/memorix-descriptors/types` | `MemorixSliceDescriptor`, `MemorixTarget`, `GraphRunEntry`, `MemorixRecordRef`, `PersistencyTarget`, … | Type-only imports |
| `@x12i/memorix-descriptors/catalog` | `defaultIdFieldForTarget`, `resolveEntityIdentityField`, `MEMORIX_INVENTORY_POLICIES_CATALOG`, … | Catalog ids |
| `@x12i/memorix-descriptors/collections/parse` | `parseMemorixCollectionName` | Last-dash collection naming |
| `@x12i/memorix-descriptors/validation/slice-shape` | `isMemorixSliceDescriptor`, `validateMemorixSliceDescriptorShape` | Slice shape validation |
| `@x12i/memorix-descriptors/validation/inventory-policy` | `DEFAULT_INVENTORY_POLICY`, `isCollectionIgnored`, … | Inventory ignore policy |

```typescript
import type { MemorixSliceDescriptor } from "@x12i/memorix-descriptors/types";
import { defaultIdFieldForTarget, resolveEntityIdentityField } from "@x12i/memorix-descriptors/catalog";
import { resolveContentTypeCardinality } from "@x12i/memorix-descriptors";
import { parseMemorixCollectionName } from "@x12i/memorix-descriptors/collections/parse";
```

Write-side / CLI / Metadata Studio hosts should import from `"."` or use `MemorixDescriptorAdmin`.

---

## Bootstrap

### From a bound Catalox client

```typescript
import { bindCataloxContext } from "@x12i/catalox";
import {
  createMemorixCataloxFromEnv,
  createMemorixDescriptorAdmin,
  resolveMemorixAppId,
} from "@x12i/memorix-descriptors";

const { catalox } = await createMemorixCataloxFromEnv();
const bound = bindCataloxContext(catalox, {
  appId: resolveMemorixAppId(), // CATALOX_APP_ID → MEMORIX_APP_ID → "memorix"
  actor: { type: "service", id: "memorix-descriptors" },
  superAdmin: true,
});

const admin = createMemorixDescriptorAdmin({
  catalox: bound,
  processEnv: process.env,
});
```

### From environment

```typescript
import { createMemorixDescriptorAdminFromEnv } from "@x12i/memorix-descriptors";

const admin = await createMemorixDescriptorAdminFromEnv();
// Uses MONGO_URI + MEMORIX_CATALOX_DB (default memorix-catalox) for Catalox metadata
// Uses MONGO_URI + Xronox when set (for reconcile / ensureCollection)
```

### Control plane (retrieval + descriptors)

For Explorer-like hosts that need both read and write stacks with shared `catalox`, `xronox`, and `appId`:

```typescript
import { createMemorixControlPlaneFromEnv } from "@x12i/memorix-descriptors";

const { retrieval, descriptors } = await createMemorixControlPlaneFromEnv();
// retrieval.client  — MemorixRetrievalClient (buildMemorixUnifiedInventory, fetchList, …)
// descriptors       — MemorixDescriptorAdmin (mutations, reconcile, applyReconcileDraft)
```

Uses shared Catalox + optional Xronox from env — no `@x12i/memorix-retrieval` import. The `retrieval` key exposes a compatible `{ appId, cataloxBound, catalox, client }` stack for inventory/reconcile wiring; `client` is a `UnifiedInventoryContext` for `buildMemorixUnifiedInventory`.

---

## Catalog object types and storage targets

Memorix overloads the word **entity**. Use this split:

| Concept | Examples | Where |
|---------|----------|--------|
| **Catalog object type** | `assets`, `vulnerabilities`, `variabilities-groups` | Catalox `memorix-entity-descriptors` (field `entityName` today) |
| **Storage target** | `entity`, `event`, `knowledge` | Descriptor field `target` → Mongo DB |

| Target | Default DB | Identity field | Typical object types |
|--------|------------|----------------|----------------------|
| `entity` | `memorix-entities` | `entityId` | `assets`, `variabilities-groups` |
| `event` | `memorix-events` | `eventId` | `vulnerabilities` (findings / timeline) |
| `knowledge` | `memorix-knowledge` | `knowledgeId` | `knowledge-playbooks`, `content-documents` |

**Not the same:** Mongo DB `memorix-entities`, legacy Catalox catalog `memorix-entities`, and catalog `memorix-entity-descriptors` are three different things. Full glossary and rename roadmap: [`docs/MEMORIX-OBJECT-TYPES-AND-TARGETS.md`](docs/MEMORIX-OBJECT-TYPES-AND-TARGETS.md).

This package accepts `target: "knowledge"` on object type descriptors and mappings. Canonical specs include `content-documents` (`snapshots`, `extractions`, `chunks`). Corpus conventions: [`memorix-corpus/README.md`](../memorix-corpus/README.md).

---

## Catalox catalogs

Do not introduce parallel catalog ids (per contracts).

| Catalog id | Descriptor type | Managed by |
|------------|-----------------|------------|
| `memorix-entity-descriptors` | Object type descriptor (legacy catalog id) | ✅ |
| `lists` | List descriptor (entity + workspace) | ✅ |
| `memorix-item-descriptors` | Item descriptor | ✅ |
| `memorix-completion-mappings` | Completion source → target maps | ✅ (optional catalog; missing catalog is treated as empty) |

App id: `memorix` (override via `CATALOX_APP_ID` / `MEMORIX_APP_ID`).

---

## `MemorixDescriptorAdmin` API

All mutating methods accept `DescriptorMutationOptions` and return `DescriptorMutationResult<T>`.

### Mutation options

| Option | Description |
|--------|-------------|
| `dryRun` | Validate + build projected graph; no Catalox write |
| `skipMongo` | Skip collection inventory / ensure steps |
| `scope` | Catalox seed scope (`domains`, `agents`) merged into item data |
| `allowCreateCollection` | When `true` and Xronox is configured, report `create-required` for missing collections (does not move data) |
| `cascade` | Property/relation removal updates list/item field sets (default `true`) |
| `force` | Hard delete / ignore default list-item guards |
| `mode` | `soft` \| `hard` for `removeEntityDescriptor` |
| `reason` | Reserved for audit trail (not persisted yet) |
| `expectedVersion` | Reserved for optimistic concurrency (not enforced yet) |

### Mutation result

```typescript
{
  ok: boolean;           // false if validation/integrity errors on the projected graph
  dryRun: boolean;
  data?: T;
  warnings: string[];
  errors: Array<{ code: string; message: string; path?: string }>;
  cataloxWrites: Array<{ catalogId; itemId; action: "create"|"update"|"delete" }>;
  mongoActions?: Array<{ action; database; collection }>;
  integrity: DescriptorIntegrityReport;
  rollbackPlan?: CataloxWriteAction[];  // reserved — not populated on failed writes yet
}
```

Every mutation validates against a **projected** full descriptor graph (in-memory) before writing.

---

### Integrity & graph

| Method | Description |
|--------|-------------|
| `validateMemorixDescriptors()` | Load all catalogs + run referential integrity |
| `getDescriptorGraph()` | Nodes/edges: entities, lists, items, mappings, relations |
| `getDescriptorGraphMermaid()` | Mermaid diagram string |
| `loadSnapshot()` | Raw `DescriptorSnapshot` from Catalox |

**Integrity checks include:** default list/item ids exist; list fields ⊆ entity properties with `humanReadable: true`; item fields human-readable; content types on properties/lists/items; relation targets; mapping `targetCollection` / `entityType`; forbidden `process.env` in descriptor JSON; dangling relation references.

Also exported: `validateDescriptorSnapshot`, `buildDescriptorGraph`, `descriptorGraphToMermaid`.

---

### Entity descriptors

| Method | Description |
|--------|-------------|
| `registerEntityDescriptor(input)` | Create entity + default `{entity}-main-list` + `{entity}-detail-item` (atomic logical transaction) |
| `updateEntityDescriptor(entityName, patch)` | Patch defaults, identity, content types, etc. **Cannot** rename `entityName` / `id` |
| `removeEntityDescriptor(entityName, options?)` | `soft` (default): deprecated flags; `hard`: delete entity + all list/item for that type, scrub inbound relations / inventory-policy refs / mappings / write descriptors |

`registerEntityDescriptor` input: `entityName`, `target`, `collectionPrefix`, `canonicalContentType`, optional `identity`, `initialProperties`, `scope`.

---

### Content types & collections

Content types live under `entityDescriptor.contentTypes` (each resolves to a Mongo collection).

**Entity-level join key (`identityField`):** Primary FK linking all content-type collections of an object type. When omitted, defaults to the storage-target id field (`entityId`, `eventId`, or `knowledgeId`). Set explicitly for non-standard keys (e.g. `subnetId` on `subnets`). Resolve at runtime with `resolveEntityIdentityField(entity)`.

**Content-type cardinality (`cardinality`):** `1:1` = one document per identity; `1:n` = many per identity. When omitted: `core` / `snapshots` postfixes → `1:1`; others → `1:n`. Resolve with `resolveContentTypeCardinality(ct, contentTypeKey)`.

| Method | Description |
|--------|-------------|
| `addContentType(entityName, input)` | Add key; at most one `isCanonical: true` |
| `updateContentType(entityName, key, patch)` | Patch postfix, collection, canonical flag, etc. |
| `removeContentType(entityName, key)` | Fails if referenced by properties, lists, items, relations, or sole canonical |
| `resolveCollection(entityName, contentTypeKey)` | Same resolution order as retrieval (explicit → env → `{prefix}-{postfix}`) |
| `ensureCollection(entityName, contentTypeKey)` | Inventory via Xronox `listCollections` |
| `planCollectionRename(entityName, contentTypeKey, newName)` | Migration plan only (no data move) |

---

### Properties

| Method | Description |
|--------|-------------|
| `addProperty(entityName, key, property, options?)` | Validates `source.contentType`; optional `addToDefaultList` / `addToDefaultItem` |
| `updateProperty(entityName, key, patch)` | Merge patch |
| `removeProperty(entityName, key, options?)` | Cascades out of list `fields[]` and item `sections[].fields[]` unless `cascade: false`; fails if used in relation join paths |

---

### List descriptors

| Method | Description |
|--------|-------------|
| `createListDescriptor(input)` | Entity list (`kind` omitted or `"entity"`) or workspace list (`kind: "workspace"`) |
| `updateListDescriptor(id, patch)` | |
| `removeListDescriptor(id)` | Blocked if still entity default unless `force` |
| `setEntityDefaultList(entityName, listDescriptorId)` | Wire `defaultListDescriptorId` with referential checks |
| `addListExtension(listId, extension)` | Add multi-collection join |
| `removeListExtension(listId, contentType)` | |
| `addListAnalyticsField(listId, field)` | Add computed analytics column |
| `removeListAnalyticsField(listId, field)` | |
| `setListSort(listId, sort[])` | Set compound `defaultSort` |

Also dispatchable via `executeDescriptorMutation` / `memorix-catalox-api POST /metadata/mutations`.

### Item descriptors

| Method | Description |
|--------|-------------|
| `createItemDescriptor(input)` | |
| `updateItemDescriptor(id, patch)` | |
| `removeItemDescriptor(id)` | Blocked if still entity default unless `force` |
| `setEntityDefaultItem(entityName, itemDescriptorId)` | |

---

### Slice descriptors

Slice descriptors are list-catalog items with `kind: "slice"` (inventory-driven list views).

| Method | Description |
|--------|-------------|
| `createSliceDescriptor(input)` | Validate shape + snapshot refs; write to list catalog |
| `updateSliceDescriptor(sliceId, patch)` | |
| `removeSliceDescriptor(sliceId)` | |
| `validateSliceDescriptor(sliceId)` | Shape + entity/field/content-type checks |
| `previewSliceDescriptor(sliceId)` | Dry-run preview payload |

Also exported: `validateMemorixSliceDescriptorShape`, `validateSliceDescriptorAgainstSnapshot`, `buildSlicePreview` (main entry and `./validation/slice-shape` for shape-only helpers).

---

### Inventory policy

Ignored collections are stored in Catalox catalog `memorix-inventory-policies` (item id `default`).

| Method | Description |
|--------|-------------|
| `getInventoryPolicy()` | Load current policy |
| `updateInventoryPolicy(patch)` | Replace `ignoredCollections` |
| `ignoreCollection(input)` | Append ignored entry |
| `unignoreCollection(input)` | Remove ignored entry |

Also exported from `./validation/inventory-policy`: `DEFAULT_INVENTORY_POLICY`, `validateMemorixInventoryPolicy`, `isCollectionIgnored`, `ignoredCollectionKey`.

---

### Relations

Relations live on the **source** entity descriptor under `relations`.

| Method | Description |
|--------|-------------|
| `addRelation(sourceEntity, relationKey, descriptor)` | Validates target entity + content types |
| `updateRelation(sourceEntity, relationKey, patch)` | |
| `removeRelation(sourceEntity, relationKey, options?)` | Cascades `includeRelations` on list/item unless `cascade: false` |

`RelationInput`: `targetEntity`, `type`, `source`/`target` `{ contentType, path }`, optional `defaultMode`, `defaultArrayProperty`, `targetFields`.

---

### Completion mappings

| Method | Description |
|--------|-------------|
| `registerMapping(input)` | Cross-validates `entityType`, `target`, `targetCollection`, field paths |
| `updateMapping(id, patch)` | |
| `removeMapping(id)` | |

Shape: `MemorixCompletionMapping` (see `src/types/index.ts` and `MEMORIX-DATABASE-CONVENTIONS.md`).

---

### Seed import / export

Compatible with the standard Memorix `catalox-seeds/inputs/` layout (also used by `@x12i/memorix-retrieval`):

```
catalox-seeds/inputs/
  entity-descriptors/*.json
  list-descriptors/*.json
  item-descriptors/*.json
  mapping-descriptors/*.json   # optional
  scope.json
  manifest.json                # written on export
```

| Method | Description |
|--------|-------------|
| `buildDescriptorSeedManifest(scope?)` | Build manifest from current Catalox snapshot |
| `exportDescriptorSeedInputs(dir, scope?)` | Write inputs layout to disk |
| `importDescriptorSeedInputs(dir, { dryRun?, scope? })` | Validate + upsert |
| `applyDescriptorSeedManifest(manifest, { dryRun?, force? })` | Upsert manifest items (scope merged into data) |

Uses local `mergeSeedItemScopeIntoUpsertData` for apply shape.

---

### Mongo reconciliation

Uses **`buildMemorixUnifiedInventory`** implemented in this package (same shape as retrieval's Inventory UI). Legacy `buildMemorixCollectionInventory` is no longer used. Hosts may pass pre-built inventory from `@x12i/memorix-retrieval` or build via `admin.reconcileDescriptorsWithMongo()`.

```typescript
const result = await admin.reconcileDescriptorsWithMongo({
  inventory: unifiedFromRetrieval, // optional — pass what Inventory already loaded
  inventoryOptions: { sourceLens: "catalox-first", includeExactCounts: true },
  allowCreateCollection: true,       // create missing Mongo collections (native driver)
  autoRegisterOrphans: true,         // apply register-entity drafts for parseable orphans
  skipMongo: true,                   // suggestions only, no Mongo side effects
});
```

**Returns:**

```typescript
{
  inventory: MemorixUnifiedInventory;
  issues: MemorixInventoryIssue[];   // same as inventory.issues
  suggestions: ReconcileSuggestion[];
  applied: Array<{ kind: string; detail: string }>;
}
```

**Suggestion kinds** (aligned with unified inventory issues):

| Kind | Typical issue | Action |
|------|---------------|--------|
| `ensure-collection` | `missing-collection` | Create Mongo collection for declared content type |
| `empty-declared-collection` | `empty-declared-collection` | Informational |
| `create-descriptor-for-orphan` | `orphan-collection`, `object-missing` | Register new entity descriptor |
| `add-content-type-for-orphan` | `content-type-missing` | Add content type to existing entity |
| `fix-collection-binding` | `collection-mismatch` | Patch content type `collection` binding |
| `ignore-collection` | `unparseable-collection` | Add to inventory ignore policy |
| `unparseable-collection` | `unparseable-collection` | Informational / manual review |

Requires `MONGO_URI` and Xronox when building inventory internally (via `createMemorixDescriptorAdminFromEnv` or `createMemorixControlPlaneFromEnv`).

---

### Reconcile drafts & apply

Draft builders produce a **`ReconcileDescriptorDraft`** (`suggestedAction` + `draft` payload + `warnings`). Metadata Studio flow: **build draft → dry-run preview → apply**.

| Builder | `suggestedAction` |
|---------|-------------------|
| `buildDescriptorDraftFromOrphanCollection(input)` | `registerEntityDescriptor` |
| `buildAddContentTypeDraftFromCollection(input, snapshot?)` | `addContentType` |
| `buildFixCollectionBindingDraft(input, snapshot?, env?)` | `updateContentType` |
| `buildIgnoreCollectionDraft(input)` | `ignoreCollection` |

| Method | Description |
|--------|-------------|
| `buildReconcileDraftForSuggestion(suggestion, snapshot?, env?)` | Map a `ReconcileSuggestion` to a draft (uses builders above) |
| `applyReconcileDraft(ctx, draft, options?)` | Low-level apply (also `admin.applyReconcileDraft(draft, options?)`) |

Supported apply actions: `registerEntityDescriptor`, `addContentType`, `updateContentType`, `ignoreCollection`. `manualReview` drafts return `MANUAL_REVIEW_REQUIRED`.

### Authored narratives catalog (`narratives`)

Optional map on each object-type descriptor, keyed by narrative slug. Each entry uses `MemorixNarrativeDefinition`:

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `having-signal` \| `association-common-property` | Required classification |
| `label` | `string` | Optional display label |
| `description` | `string` | Optional UI description |
| `sourceRef` | `string` | Association source (e.g. `assetVulnerabilities`) |
| `targetEntity` | `string` | Target object type (e.g. `vulnerabilities`) |
| `property` | `string` | For `association-common-property` — matched property |
| `value` | `string` | For `association-common-property` — matched value |

Persist via `updateEntityDescriptor(entity, { narratives })` or seed JSON under `entity-descriptors/*.json`. Validated by `validateNarrativesCatalog` / `validateNarrativeDefinition`. Survives the v2 Catalox round-trip and appears on `loadSnapshot().entityDescriptors[<entity>].narratives`.

Per-record narrative **instances** (`addedAt`, `detectedBy`, etc.) remain on Mongo documents at `narratives.<key>` — they are not part of this catalog.

### Root envelope property catalog (`rootPropertyCatalog`)

Pipeline-computed catalog of root/envelope keys (outside `data`) per content type. Distinct from declarative `rootProperties` (managed snapshot buckets like `possiblePaths` on subnets).

Each `MemorixContentTypeRootProperties` entry includes `contentType`, `properties[]` (`key`, `valueTypes`, `presentCount`, `totalCount`, optional `internal`), `computedAt`, and `fullScan`.

| Method | Description |
|--------|-------------|
| `computeRootPropertyCatalog(objectType, options?)` | Full-scan Mongo collections per content type; persist on descriptor. Options: `ttlMs` (default 24h), `contentTypes[]`, `force` |

Also exported: `scanRootPropertyCatalogForDescriptor`, `mergeRootPropertyStatsFromDocs`, `DEFAULT_ROOT_PROPERTY_CATALOG_TTL_MS`.

```typescript
const result = await admin.computeRootPropertyCatalog("assets", { force: true });
// result.rootPropertyCatalog — persisted when not skipped by TTL
```

```typescript
const { suggestions } = await admin.reconcileDescriptorsWithMongo({ skipMongo: true });
const snapshot = await admin.loadSnapshot();
const draft = buildReconcileDraftForSuggestion(suggestions[0]!, snapshot);
const preview = await admin.applyReconcileDraft(draft!, { dryRun: true });
const applied = await admin.applyReconcileDraft(draft!, { dryRun: false });
```

Also on admin (with snapshot-aware validation): `buildDescriptorDraftFromOrphanCollection`, `buildAddContentTypeDraftFromCollection`, `buildFixCollectionBindingDraft`, `buildIgnoreCollectionDraft`.

---

### Typed mutation dispatcher (optional)

Single entry for hosts that route mutation requests by operation name (Explorer `POST /metadata/mutations`, CLI, CI):

```typescript
import { executeDescriptorMutation } from "@x12i/memorix-descriptors";

const result = await executeDescriptorMutation(admin, {
  operation: "addContentType",
  input: { entityName: "assets", contentType: { key: "enrichment", postfix: "enrichment", collection: "assets-enrichment" } },
  options: { dryRun: true },
});
```

Dispatches to `MemorixDescriptorAdmin` methods including `applyReconcileDraft`. Unknown operations return `UNKNOWN_OPERATION`.

---

## CLI

Binary: `memorix-descriptors` (after `npm run build`).

| Command | Status |
|---------|--------|
| `validate` | ✅ |
| `reconcile` | ✅ (needs Xronox / `MONGO_URI`; JSON includes `inventory`, `issues`, `suggestions`, `applied`) |
| `graph [--format mermaid]` | ✅ |
| `entity register --file <json> [--dry-run]` | ✅ |
| `content-type add <entity> <key> --postfix <p> [--dry-run]` | ✅ |
| `list create --file <json> [--dry-run]` | ✅ |
| `list update <id> --file <json> [--dry-run]` | ✅ |
| `list add-extension <id> --file <json>` | ✅ |
| `list remove-extension <id> <contentType>` | ✅ |
| `list add-analytics <id> --file <json>` | ✅ |
| `list remove-analytics <id> <field>` | ✅ |
| `list set-sort <id> --file <json>` | ✅ |
| `item update <id> --file <json> [--dry-run]` | ✅ |
| `mapping register --file <json> [--dry-run]` | ✅ |
| `seed export <dir>` | ✅ |
| `seed import <dir> [--dry-run]` | ✅ |

```bash
npx memorix-descriptors validate
npx memorix-descriptors reconcile --allow-create-collection --auto-register-orphans
npx memorix-descriptors graph --format mermaid
npx memorix-descriptors entity register --file ./new-entity.json --dry-run
npx memorix-descriptors seed export ./catalox-seeds/inputs
npx memorix-descriptors seed import ./catalox-seeds/inputs --dry-run
```

**Library-only today** (use `MemorixDescriptorAdmin` directly): entity update/remove, property CRUD, content-type update/remove, list/item/slice create/remove, relation CRUD, mapping update/remove, inventory policy, `ensureCollection`, reconcile draft builders, `applyReconcileDraft`, `executeDescriptorMutation`, etc.

---

## Explorer / host integration

| UI surface | API |
|------------|-----|
| Entity graph canvas | `getDescriptorGraph`, `reconcileDescriptorsWithMongo` |
| Inventory / Metadata Studio reconcile | `buildMemorixUnifiedInventory` (retrieval) + `reconcileDescriptorsWithMongo`, `buildReconcileDraftForSuggestion`, `applyReconcileDraft` |
| Add entity wizard | `registerEntityDescriptor` |
| Column / field editor | `addProperty`, `updateListDescriptor` |
| Detail layout editor | `updateItemDescriptor` |
| Slice editor | `createSliceDescriptor`, `validateSliceDescriptor`, `previewSliceDescriptor` |
| Collection health | `reconcileDescriptorsWithMongo` |
| Ignore collection | `ignoreCollection`, `buildIgnoreCollectionDraft` |
| Mapping editor | `registerMapping` |
| Knowledge tab | Filter entities where `target === "knowledge"` |
| Generic mutation HTTP | `executeDescriptorMutation` (optional) |

Hosts should **read** via `@x12i/memorix-retrieval` and **write** only through this package. Pass pre-built `MemorixUnifiedInventory` into reconcile when the Inventory UI already loaded it.

---

## Validation utilities (exported)

### Main entry (`"."`)

Re-exported / extended validators (aligned with retrieval rules, plus `knowledge` target):

- `validateMemorixEntityDescriptor`, `validateMemorixListDescriptor`, `validateMemorixEntityListDescriptor`, `validateMemorixItemDescriptor`
- `validateNarrativesCatalog`, `validateNarrativeDefinition`, `validateRootPropertyCatalog`
- `validateMemorixSliceDescriptorShape`, `validateSliceDescriptorAgainstSnapshot`, `buildSlicePreview`, `isMemorixSliceDescriptor`
- `validateMemorixInventoryPolicy`, `validateIgnoredCollectionEntry`, `isCollectionIgnored`, `DEFAULT_INVENTORY_POLICY`
- `validatePropertyDescriptor`, `assertListFieldsHumanReadable`
- `defaultListDescriptorId`, `defaultItemDescriptorId`, `singularizeEntityName`, `isKebabCase`
- `parseMemorixCollectionName`, `defaultIdFieldForTarget`
- `DescriptorValidationError`

### Retrieval-safe subpaths

See [Package exports](#package-exports). Subpath entry modules avoid importing the main `"."` write-side graph.

## Environment variables

See [`.env.example`](.env.example).

| Variable | Purpose |
|----------|---------|
| `MONGO_URI` | Mongo cluster for payload data (Xronox) and Catalox metadata |
| `MEMORIX_CATALOX_DB` | Catalox metadata database (default `memorix-catalox`) |
| `CATALOX_APP_ID` / `MEMORIX_APP_ID` | App namespace (default `memorix`) |
| `MEMORIX_ENTITIES_DB`, `MEMORIX_EVENTS_DB`, `MEMORIX_KNOWLEDGE_DB` | Payload DB names |
| `MEMORIX_*_COLLECTION_<ENTITY>` | Per-entity collection overrides |
| `MEMORIX_ENSURE_DESCRIPTOR_CATALOGS=1` | Auto-create descriptor catalogs on `createMemorixDescriptorAdminFromEnv` / control plane bootstrap |
| `MONGO_LIVE_TESTS=1` or `MEMORIX_CATALOX_LIVE_TESTS=1` | Enable live integration tests |

---

## Tests

```bash
npm test          # unit tests only (no Mongo)
npm run test:live # integration tests (reads .env, needs MONGO_LIVE_TESTS=1)
npm run test:all  # both
```

Live tests: Mongo/Catalox connectivity, validate deployed graph, load smoke entity (`MEMORIX_SMOKE_ENTITY`, default `assets`), catalog listing, graph, collection resolve, dry-run `addProperty`. Reconcile live test requires `MONGO_URI` and asserts `MemorixUnifiedInventory` shape (`rows`, `issues`, `generatedAt`).

CI (`validate-descriptor-seeds` workflow) runs `npm ci` and `npm run build` with no retrieval dependency.

---

## Gap analysis (spec vs implementation)

### Implemented ✅

| Area | Coverage |
|------|----------|
| Bootstrap | `createMemorixDescriptorAdmin`, `createMemorixDescriptorAdminFromEnv`, `createMemorixControlPlaneFromEnv` |
| Package exports | Retrieval-safe subpaths (`/types`, `/catalog`, `/collections/parse`, `/validation/slice-shape`, `/validation/inventory-policy`) |
| Entity lifecycle | Register (atomic list+item), update, soft/hard remove |
| Content types | Add, update, remove with referential guards |
| Collections | Resolve, ensure (inventory), rename **plan** |
| Properties | Add, update, remove with list/item cascade |
| List / item descriptors | Full CRUD + set default list/item |
| Slice descriptors | CRUD, validate, preview |
| Inventory policy | Ignore / unignore collections in Catalox |
| Relations | Add, update, remove with list/item cascade |
| Mappings | CRUD + cross-validation (`memorix-completion-mappings`) |
| Integrity engine | Full-graph validation, dangling refs, forbidden patterns |
| Descriptor graph | Build + Mermaid export |
| Seeds | Export/import/manifest; compatible with retrieval inputs layout |
| Reconcile | Unified inventory + issues + extended suggestions; `applyReconcileDraft`; `autoRegisterOrphans`, `allowCreateCollection` |
| Mutation dispatcher | `executeDescriptorMutation` (optional) |
| Dry-run | All mutations + reconcile apply |
| Targets | `entity`, `event`, `knowledge` in types and entity validation |
| Transactions | `executeCataloxWrites` — compensating rollback on mid-batch Catalox failure; `rollbackPlan` on success |
| Audit | JSONL via `MEMORIX_DESCRIPTORS_AUDIT_LOG` or per-mutation `reason` |
| Concurrency | `expectedVersion` optimistic check on Catalox updates |
| Rename | `renameEntityDescriptor(from, to)` with optional deprecated source |
| Catalog bootstrap | `ensureMemorixDescriptorCatalogs` on `createMemorixDescriptorAdminFromEnv` |
| CLI | Full CRUD for entity, content-type, property, list, item, relation, mapping + reconcile flags |
| CI seeds | `npm run validate:seeds` + GitHub workflow |
| Tests | Unit + live Catalox suite |

### Partial ⚠️

| Item | Notes |
|------|------|
| Mutation `ok` vs `graphValid` | `ok` = Catalox write succeeded; `graphValid` = projected snapshot passes integrity (legacy graphs may differ) |
| `ensureCollection` via Xronox | Still inventory-only when using Xronox adapter; reconcile/create uses native `mongodb` driver |
| Completion mappings catalog | Gracefully empty if catalog not seeded in Catalox yet |
| Retrieval seed npm scripts | Still owned by `@x12i/memorix-retrieval`; this package provides equivalent CLI `seed` commands |
| Impact report command | Use `validateMemorixDescriptors` + dry-run `removeEntityDescriptor` |

### Not implemented yet ❌

| Item | Notes |
|------|------|
| Standalone `impact-report` CLI subcommand | Compose validation + dry-run helpers manually today |

### Explicit non-goals (v1)

- Writing Memorix **payload** documents (stays in `@x12i/memorix-completion`)
- Replacing Catalox storage or building Explorer UI
- Runtime list/item composition (stays in `@x12i/memorix-retrieval`)

---

## Implementation status by phase

| Phase | Focus | Status |
|-------|--------|--------|
| **1** | Bootstrap, validation, entity/content/property CRUD, seeds, dry-run | ✅ Done |
| **2** | List/item/relation CRUD, cascades, graph | ✅ Done |
| **3** | Mappings catalog, Mongo reconcile (unified inventory) | ✅ Done |
| **4** | Knowledge target, slice descriptors, inventory policy, subpath exports | ✅ Done |
| **5** | Rename/migrate, audit, rollback, CLI parity, CI seeds, reconcile apply | ✅ Done (impact-report CLI optional) |

---

## Related packages

- **@x12i/memorix-retrieval** — read/composition layer (discover, fetchList, fetchItem, **`buildMemorixUnifiedInventory`**)
- **@x12i/catalox** — descriptor storage and native catalog APIs
- **@x12i/memorix-completion** — Memorix payload document writes (not descriptors)
- **@x12i/xronox** — optional Mongo inventory for reconcile and `ensureCollection`

---
_Source: https://npm.io/package/@x12i/memorix-descriptors · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
