@x12i/memorix-retrieval 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +64 -24
  2. package/catalox-seeds/inputs/entity-descriptors/assets.json +2 -0
  3. package/catalox-seeds/inputs/entity-descriptors/variabilities-groups.json +2 -0
  4. package/catalox-seeds/inputs/entity-descriptors/vulnerabilities.json +2 -0
  5. package/catalox-seeds/memorix-retrieval-descriptors.manifest.json +6 -0
  6. package/dist/client/create-client.js +2 -2
  7. package/dist/client/create-client.js.map +1 -1
  8. package/dist/client/create-stack-from-env.js +4 -6
  9. package/dist/client/create-stack-from-env.js.map +1 -1
  10. package/dist/client/types.d.ts +4 -0
  11. package/dist/client/types.d.ts.map +1 -1
  12. package/dist/descriptors/descriptor-types.d.ts +4 -0
  13. package/dist/descriptors/descriptor-types.d.ts.map +1 -1
  14. package/dist/descriptors/discover-entities.d.ts +3 -1
  15. package/dist/descriptors/discover-entities.d.ts.map +1 -1
  16. package/dist/descriptors/discover-entities.js +14 -43
  17. package/dist/descriptors/discover-entities.js.map +1 -1
  18. package/dist/descriptors/resolve-app-id.d.ts +4 -0
  19. package/dist/descriptors/resolve-app-id.d.ts.map +1 -0
  20. package/dist/descriptors/resolve-app-id.js +8 -0
  21. package/dist/descriptors/resolve-app-id.js.map +1 -0
  22. package/dist/descriptors/resolve-default-descriptors.d.ts +11 -0
  23. package/dist/descriptors/resolve-default-descriptors.d.ts.map +1 -0
  24. package/dist/descriptors/resolve-default-descriptors.js +22 -0
  25. package/dist/descriptors/resolve-default-descriptors.js.map +1 -0
  26. package/dist/descriptors/validate-descriptor.d.ts.map +1 -1
  27. package/dist/descriptors/validate-descriptor.js +11 -0
  28. package/dist/descriptors/validate-descriptor.js.map +1 -1
  29. package/dist/explorer/entity-graph.d.ts +3 -1
  30. package/dist/explorer/entity-graph.d.ts.map +1 -1
  31. package/dist/explorer/entity-graph.js +4 -0
  32. package/dist/explorer/entity-graph.js.map +1 -1
  33. package/dist/index.d.ts +12 -3
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +11 -3
  36. package/dist/index.js.map +1 -1
  37. package/dist/retrieval/fetch-for-entity.d.ts +18 -0
  38. package/dist/retrieval/fetch-for-entity.d.ts.map +1 -0
  39. package/dist/retrieval/fetch-for-entity.js +21 -0
  40. package/dist/retrieval/fetch-for-entity.js.map +1 -0
  41. package/dist/retrieval/fetch-list.d.ts.map +1 -1
  42. package/dist/retrieval/fetch-list.js +16 -1
  43. package/dist/retrieval/fetch-list.js.map +1 -1
  44. package/dist/retrieval/resolve-filters.d.ts +2 -0
  45. package/dist/retrieval/resolve-filters.d.ts.map +1 -1
  46. package/dist/retrieval/resolve-filters.js +24 -3
  47. package/dist/retrieval/resolve-filters.js.map +1 -1
  48. package/dist/retrieval/resolve-list-search.d.ts +6 -0
  49. package/dist/retrieval/resolve-list-search.d.ts.map +1 -0
  50. package/dist/retrieval/resolve-list-search.js +25 -0
  51. package/dist/retrieval/resolve-list-search.js.map +1 -0
  52. package/dist/seeds/build-manifest.d.ts +8 -0
  53. package/dist/seeds/build-manifest.d.ts.map +1 -0
  54. package/dist/seeds/build-manifest.js +83 -0
  55. package/dist/seeds/build-manifest.js.map +1 -0
  56. package/dist/seeds/check-needs-seed.d.ts +10 -0
  57. package/dist/seeds/check-needs-seed.d.ts.map +1 -0
  58. package/dist/seeds/check-needs-seed.js +66 -0
  59. package/dist/seeds/check-needs-seed.js.map +1 -0
  60. package/dist/seeds/default-seed-spec.d.ts +12 -0
  61. package/dist/seeds/default-seed-spec.d.ts.map +1 -0
  62. package/dist/seeds/default-seed-spec.js +89 -0
  63. package/dist/seeds/default-seed-spec.js.map +1 -0
  64. package/dist/seeds/merge-for-apply.d.ts +28 -0
  65. package/dist/seeds/merge-for-apply.d.ts.map +1 -0
  66. package/dist/seeds/merge-for-apply.js +31 -0
  67. package/dist/seeds/merge-for-apply.js.map +1 -0
  68. package/dist/seeds/paths.d.ts +10 -0
  69. package/dist/seeds/paths.d.ts.map +1 -0
  70. package/dist/seeds/paths.js +40 -0
  71. package/dist/seeds/paths.js.map +1 -0
  72. package/dist/seeds/seed-types.d.ts +57 -0
  73. package/dist/seeds/seed-types.d.ts.map +1 -0
  74. package/dist/seeds/seed-types.js +2 -0
  75. package/dist/seeds/seed-types.js.map +1 -0
  76. package/dist/tests/descriptor-validation.test.js +15 -0
  77. package/dist/tests/descriptor-validation.test.js.map +1 -1
  78. package/dist/tests/fixtures.d.ts.map +1 -1
  79. package/dist/tests/fixtures.js +2 -0
  80. package/dist/tests/fixtures.js.map +1 -1
  81. package/dist/tests/relations.test.js +3 -0
  82. package/dist/tests/relations.test.js.map +1 -1
  83. package/dist/tests/resolve-app-id.test.d.ts +2 -0
  84. package/dist/tests/resolve-app-id.test.d.ts.map +1 -0
  85. package/dist/tests/resolve-app-id.test.js +19 -0
  86. package/dist/tests/resolve-app-id.test.js.map +1 -0
  87. package/dist/tests/resolve-filters.test.d.ts +2 -0
  88. package/dist/tests/resolve-filters.test.d.ts.map +1 -0
  89. package/dist/tests/resolve-filters.test.js +47 -0
  90. package/dist/tests/resolve-filters.test.js.map +1 -0
  91. package/dist/tests/seed-check.test.d.ts +2 -0
  92. package/dist/tests/seed-check.test.d.ts.map +1 -0
  93. package/dist/tests/seed-check.test.js +47 -0
  94. package/dist/tests/seed-check.test.js.map +1 -0
  95. package/dist/tests/seed-manifest.test.d.ts +2 -0
  96. package/dist/tests/seed-manifest.test.d.ts.map +1 -0
  97. package/dist/tests/seed-manifest.test.js +80 -0
  98. package/dist/tests/seed-manifest.test.js.map +1 -0
  99. package/docs/DATA-TIER-CONTRACT.md +138 -0
  100. package/docs/EXPLORER-HOST-APIS.md +8 -6
  101. package/docs/MEMORIX-CATALOX-CONTRACTS.md +540 -0
  102. package/package.json +4 -3
@@ -0,0 +1,540 @@
1
+ # Memorix ↔ Catalox contracts
2
+
3
+ **Canonical sync document** for all Memorix data packages and Memorix-consuming UIs (Explorer, completion pipelines, ingestion, graph tools).
4
+
5
+
6
+ | Document | Scope |
7
+ |----------|--------|
8
+ | **This file** | Catalox catalogs, descriptor JSON formats, expected Mongo shape, cross-component behavior |
9
+ | [MEMORIX-DATABASE-CONVENTIONS.md](./MEMORIX-DATABASE-CONVENTIONS.md) | Database names, collection naming, env vars, record envelope |
10
+ | [DATA-TIER-CONTRACT.md](./DATA-TIER-CONTRACT.md) | What host apps may call (retrieval APIs only) |
11
+
12
+ **Reference implementation:** `@x12i/memorix-retrieval` — descriptor types in `src/descriptors/descriptor-types.ts`, seeds in `catalox-seeds/inputs/`, validation in `src/descriptors/validate-descriptor.ts`.
13
+
14
+ If this document and a package disagree, **update both together**.
15
+
16
+ ---
17
+
18
+ ## 1. Split of responsibilities
19
+
20
+ ```text
21
+ ┌─────────────────────────────────────────────────────────────────┐
22
+ │ Catalox (metadata) │
23
+ │ • What entities exist │
24
+ │ • How to list / detail / relate them │
25
+ │ • Property paths, filters, sorts, relations │
26
+ └───────────────────────────────┬─────────────────────────────────┘
27
+ │ listCatalogItems / getCatalogItem
28
+
29
+ ┌─────────────────────────────────────────────────────────────────┐
30
+ │ @x12i/memorix-retrieval (data tier) │
31
+ │ • Load descriptors from Catalox │
32
+ │ • Resolve Mongo DB + collection from descriptor │
33
+ │ • Read, compose, paginate, multi-match, relations, content │
34
+ └───────────────────────────────┬─────────────────────────────────┘
35
+ │ fetchMemorixList / fetchMemorixItem / graph
36
+
37
+ ┌─────────────────────────────────────────────────────────────────┐
38
+ │ Host apps (Explorer, APIs, tools) │
39
+ │ • Never bypass descriptors for entity I/O │
40
+ │ • Never invent entity lists from env or collection names │
41
+ └─────────────────────────────────────────────────────────────────┘
42
+
43
+ ┌─────────────────────────────────────────────────────────────────┐
44
+ │ MongoDB (payload) │
45
+ │ • memorix-entities / memorix-events │
46
+ │ • Domain documents under `data` │
47
+ └─────────────────────────────────────────────────────────────────┘
48
+ ```
49
+
50
+ | Layer | Owns | Must not own |
51
+ |-------|------|--------------|
52
+ | **Catalox** | Descriptor catalogs, item ids, scope tags, graph semantics | Raw Mongo queries, business payload writes |
53
+ | **memorix-retrieval** | Descriptor-driven reads, composition, validation | Ad hoc catalog types outside the three Memorix catalogs |
54
+ | **Completion / ingestion** | Writing `data` on Memorix records per [MEMORIX-DATABASE-CONVENTIONS](./MEMORIX-DATABASE-CONVENTIONS.md) | Defining list columns or UI field layouts (that's descriptors) |
55
+ | **Explorer / UIs** | Presentation, routing, design model | Direct Mongo, xmemory-store, env-based entity discovery |
56
+
57
+ ---
58
+
59
+ ## 2. Catalox app and catalogs
60
+
61
+ ### 2.1 App id
62
+
63
+ | Constant | Value |
64
+ |----------|-------|
65
+ | Default `appId` | `memorix` |
66
+ | Env override | `CATALOX_APP_ID` (fallback: `MEMORIX_APP_ID`) |
67
+
68
+ All Memorix descriptor catalogs live under this app. Peers must use the same `appId` unless a deployment explicitly namespaces apps. `createMemorixRetrieval`, `createMemorixRetrievalFromEnv`, and `createMemorixRetrievalStackFromEnv` resolve the app id via `resolveMemorixAppId()`.
69
+
70
+ ### 2.2 Catalog ids (only these three)
71
+
72
+ | Catalog id | Item id field | Title field | Purpose |
73
+ |------------|---------------|-------------|---------|
74
+ | `memorix-entity-descriptors` | `id` | `entityName` | Entity schema: properties, content types, relations, default list/item |
75
+ | `memorix-list-descriptors` | `id` | `title` | Tabular list views |
76
+ | `memorix-item-descriptors` | `id` | `title` | Record detail layout |
77
+
78
+ Catalogs use **`sourceMode: "native"`** and **`catalogType: "memorix"`** in seed manifests. Do not introduce parallel catalog names (e.g. `memorix_entity_content_types`) for new work.
79
+
80
+ ### 2.3 Item shape in Catalox
81
+
82
+ Each catalog item is stored as:
83
+
84
+ ```json
85
+ {
86
+ "id": "<descriptor-id>",
87
+ "...": "<descriptor fields — see §3>",
88
+ "scope": {
89
+ "domains": ["network", "vulnerabilities"],
90
+ "agents": ["neo"]
91
+ }
92
+ }
93
+ ```
94
+
95
+ - **`id`** must match the descriptor's own `id` field.
96
+ - **`scope`** is applied at seed time from `catalox-seeds/inputs/scope.json`. All items in a seed bundle share the same scope unless a future manifest explicitly overrides per item.
97
+ - Descriptor payload lives in the item **`data`** body when read via Catalox APIs.
98
+ - **Seed apply note:** the Catalox seed CLI upserts `row.data` only, so `scripts/apply-seed-manifest.mjs` merges `scope` into the upsert `data` object. Hosts may see `scope` inside `data` until Catalox stores top-level scope natively; retrieval ignores unknown fields when validating descriptors.
99
+
100
+ ### 2.4 Expected Catalox client behavior
101
+
102
+ Peers that integrate with Catalox must support:
103
+
104
+ | Operation | Used for |
105
+ |-----------|----------|
106
+ | `listCatalogItems(context, catalogId, { limit, offset })` | Entity discovery, paginated catalog reads |
107
+ | `getCatalogItem(context, catalogId, itemId)` | Load list/item/entity descriptor by id |
108
+
109
+ **Discovery rule:** entity existence comes **only** from listing `memorix-entity-descriptors`. There is no env fallback (`MEMORIX_ENTITY_NAMES` and similar bypasses are forbidden in retrieval and hosts).
110
+
111
+ If the catalog is empty or Catalox is unreachable, discovery returns `source: "none"` with a hint to run the seed apply script.
112
+
113
+ ---
114
+
115
+ ## 3. Descriptor formats
116
+
117
+ Types below mirror `MemorixEntityDescriptor`, `MemorixListDescriptor`, and `MemorixItemDescriptor` in `@x12i/memorix-retrieval`.
118
+
119
+ ### 3.1 Entity descriptor (`memorix-entity-descriptors`)
120
+
121
+ **Required top-level fields:**
122
+
123
+ | Field | Type | Rules |
124
+ |-------|------|-------|
125
+ | `id` | string | Stable catalog item id; usually equals `entityName` |
126
+ | `entityName` | string | Kebab-case domain id (`vulnerabilities`, `variabilities-groups`, `assets`) |
127
+ | `defaultListDescriptorId` | string | **Option A** — id in `memorix-list-descriptors` |
128
+ | `defaultItemDescriptorId` | string | **Option A** — id in `memorix-item-descriptors` |
129
+ | `collectionPrefix` | string | Used with content-type `postfix` when `collection` is omitted |
130
+ | `identity` | object | See below |
131
+ | `defaults` | object | Canonical content type, `dataRoot`, effective-date paths |
132
+ | `contentTypes` | object | Named content slices (see §3.4) |
133
+ | `properties` | object | Field dictionary for list/item composition |
134
+
135
+ **Optional:**
136
+
137
+ | Field | Type | Default |
138
+ |-------|------|---------|
139
+ | `target` | `"entity"` \| `"event"` | `"entity"` — selects `memorix-entities` vs `memorix-events` |
140
+ | `contentDefaults` | object | Default content-object storage/format |
141
+ | `relations` | object | Cross-entity joins declared on this entity |
142
+
143
+ **Identity block:**
144
+
145
+ ```json
146
+ {
147
+ "allowedIdFields": ["entityId", "eventId"],
148
+ "requiredExactlyOne": true,
149
+ "defaultIdField": "entityId"
150
+ }
151
+ ```
152
+
153
+ **Defaults block:**
154
+
155
+ ```json
156
+ {
157
+ "canonicalContentType": "snapshots",
158
+ "dataRoot": "data",
159
+ "effectiveDatePath": "capturedAt",
160
+ "fallbackEffectiveDatePaths": ["snapshot.capturedAt", "data.enrichment.enrichedAt"]
161
+ }
162
+ ```
163
+
164
+ **Property descriptor** (each key under `properties`):
165
+
166
+ ```json
167
+ {
168
+ "label": "Asset IP",
169
+ "source": { "contentType": "snapshots", "path": "data.assetIp" },
170
+ "humanReadable": true,
171
+ "sortable": true,
172
+ "filterable": true,
173
+ "list": true,
174
+ "item": true,
175
+ "valueType": "string"
176
+ }
177
+ ```
178
+
179
+ | `valueType` | Notes |
180
+ |-------------|-------|
181
+ | `string`, `number`, `boolean`, `date`, `datetime`, `enum`, `object`, `array` | Scalar / structural |
182
+ | `content` | Value is a `memorix-content-object` (see §3.7) |
183
+
184
+ List views may only expose properties with **`humanReadable: true`**.
185
+
186
+ **Relation descriptor** (under `relations`):
187
+
188
+ ```json
189
+ {
190
+ "targetEntity": "assets",
191
+ "type": "manyToOne",
192
+ "source": { "contentType": "snapshots", "path": "data.assetIp" },
193
+ "target": { "contentType": "snapshots", "path": "data.ip_address" },
194
+ "defaultMode": "extendFields",
195
+ "defaultArrayProperty": "vulnerabilities",
196
+ "targetFields": ["ipAddress", "hostName"]
197
+ }
198
+ ```
199
+
200
+ | `type` | Meaning |
201
+ |--------|---------|
202
+ | `oneToOne`, `oneToMany`, `manyToOne`, `manyToMany` | Graph semantics for Explorer and `includeRelations` |
203
+
204
+ Join paths are **Mongo document paths** on the respective content types, not Catalox ids.
205
+
206
+ ### 3.2 List descriptor (`memorix-list-descriptors`)
207
+
208
+ **Required:**
209
+
210
+ | Field | Rules |
211
+ |-------|-------|
212
+ | `id` | Unique list id (e.g. `vulnerabilities-main-list`) |
213
+ | `entity` | `entityName` referencing an entity descriptor |
214
+ | `leadingContentType` | Content type key used to drive pagination/filter/sort |
215
+ | `pagination` | `{ "enabled": true, "defaultLimit": 50, "maxLimit": 200 }` |
216
+ | `fields` | Non-empty array of **property keys** on the entity descriptor |
217
+
218
+ **Common optional fields:**
219
+
220
+ | Field | Purpose |
221
+ |-------|---------|
222
+ | `title` | Human label |
223
+ | `filters` | Default filters (always applied) plus allowlist for request filters; request overrides same property |
224
+ | `allowedSorts`, `defaultSort` | Sort whitelist and default |
225
+ | `extensions` | Pull extra content types by identity (see §3.5) |
226
+ | `includeRelations` | Embed relation data in list rows |
227
+ | `allowSortDrivenLeadingOverride` | Allow sort to switch pagination driver content type |
228
+
229
+ **Example filter:**
230
+
231
+ ```json
232
+ { "property": "severityLevel", "operator": "gte", "value": 4 }
233
+ ```
234
+
235
+ **Filter operators:** `eq`, `ne`, `in`, `nin`, `gt`, `gte`, `lt`, `lte`, `exists`, `regex`.
236
+
237
+ Each entity descriptor should define at least one list whose `id` equals `defaultListDescriptorId`. Additional lists (e.g. `critical-vulnerabilities-list`) are optional alternate views.
238
+
239
+ ### 3.3 Item descriptor (`memorix-item-descriptors`)
240
+
241
+ **Required:**
242
+
243
+ | Field | Rules |
244
+ |-------|-------|
245
+ | `id` | Unique item id (e.g. `vulnerability-detail-item`) |
246
+ | `entity` | `entityName` |
247
+ | `identity.idField` | `"entityId"` or `"eventId"` — must match request |
248
+ | `contentTypes` | Array of `{ contentType, required, multiMatch? }` |
249
+ | `sections` | Array of `{ id, title, fields[] }` — `fields` are property keys |
250
+
251
+ **Optional:**
252
+
253
+ | Field | Purpose |
254
+ |-------|---------|
255
+ | `includeRelations` | `{ relation, mode?, fields? }` — relation key from entity descriptor |
256
+ | `content` | `{ allowed, defaultIncludeFullContent?, allowedFields?, maxBytes? }` |
257
+
258
+ Each entity descriptor should define at least one item whose `id` equals `defaultItemDescriptorId`.
259
+
260
+ ### 3.4 Content type descriptor
261
+
262
+ Under `entity.contentTypes.<name>`:
263
+
264
+ ```json
265
+ {
266
+ "postfix": "snapshots",
267
+ "collection": "vulnerabilities-snapshots",
268
+ "dataRoot": "data",
269
+ "isCanonical": true,
270
+ "effectiveDatePath": "capturedAt",
271
+ "fallbackEffectiveDatePaths": ["snapshot.capturedAt"]
272
+ }
273
+ ```
274
+
275
+ **Collection resolution order** (retrieval):
276
+
277
+ 1. Explicit `collection` on the content type
278
+ 2. Env `MEMORIX_ENTITIES_COLLECTION_<ENTITY>` or `MEMORIX_EVENTS_COLLECTION_<ENTITY>`
279
+ 3. Heuristic: `{collectionPrefix}-{postfix}` (e.g. `assets-snapshots`)
280
+
281
+ **Database resolution:** entity descriptor `target` → `memorix-entities` or `memorix-events` (see [MEMORIX-DATABASE-CONVENTIONS](./MEMORIX-DATABASE-CONVENTIONS.md)).
282
+
283
+ Exactly one content type per entity should set **`isCanonical: true`**; it aligns with `defaults.canonicalContentType`.
284
+
285
+ ### 3.5 Content extensions (list)
286
+
287
+ ```json
288
+ {
289
+ "contentType": "enrichment",
290
+ "mode": "extendFields",
291
+ "join": { "by": "entityId" },
292
+ "multiMatch": { "strategy": "last", "effectiveDatePath": "capturedAt" },
293
+ "fields": { "enrichedAt": "data.enrichment.enrichedAt" }
294
+ }
295
+ ```
296
+
297
+ | `mode` | Behavior |
298
+ |--------|----------|
299
+ | `extendFields` | Merge selected paths onto the row |
300
+ | `array` | Attach array under `arrayProperty` |
301
+
302
+ ### 3.6 Multi-match
303
+
304
+ When multiple Mongo documents share the same identity, retrieval picks records using:
305
+
306
+ ```json
307
+ {
308
+ "strategy": "last" | "first" | "all" | "error" | "ignore",
309
+ "effectiveDatePath": "capturedAt",
310
+ "fallbackEffectiveDatePaths": ["snapshot.capturedAt"]
311
+ }
312
+ ```
313
+
314
+ Default for item fetches: **`last`** by effective date. Strategies are declared on item `contentTypes`, content-type defaults, or extension blocks.
315
+
316
+ ### 3.7 Content objects
317
+
318
+ Properties with `valueType: "content"` store a **Memorix content object** in Mongo:
319
+
320
+ ```json
321
+ {
322
+ "contentKey": "stories/asset-10-150-68-31.md",
323
+ "metadata": { "title": "Impact story" },
324
+ "preview": "First 200 chars…"
325
+ }
326
+ ```
327
+
328
+ Storage is configured on the entity (`contentDefaults`) or property (`content.storage`), never with inline credentials:
329
+
330
+ ```json
331
+ {
332
+ "provider": "gcs" | "s3",
333
+ "bucket": "memorix-content",
334
+ "prefix": "stories/",
335
+ "maxBytes": 65536,
336
+ "encoding": "utf8",
337
+ "credentialsRef": "env:MEMORIX_GCS_CREDENTIALS"
338
+ }
339
+ ```
340
+
341
+ Hosts provide **`contentReaders`** at retrieval bootstrap; list views return preview/metadata only (`listBehavior.fetchContent: false`).
342
+
343
+ ---
344
+
345
+ ## 4. What we expect to find in Mongo
346
+
347
+ ### 4.1 Record envelope
348
+
349
+ Documents in Memorix target databases follow [MEMORIX-DATABASE-CONVENTIONS](./MEMORIX-DATABASE-CONVENTIONS.md):
350
+
351
+ ```json
352
+ {
353
+ "_id": "<ObjectId>",
354
+ "recordId": "<optional>",
355
+ "entityId": "<for entity-targeted records>",
356
+ "eventId": "<for event-targeted records>",
357
+ "capturedAt": "<ISO-8601>",
358
+ "data": { "... domain fields referenced by descriptor paths ..." }
359
+ }
360
+ ```
361
+
362
+ **Peer rules:**
363
+
364
+ - Domain payload lives under **`data`** (or content-type-specific `dataRoot`, almost always `data`).
365
+ - Descriptor `source.path` values are **dot paths from the document root** (e.g. `data.assetIp`, not `assetIp` alone).
366
+ - Identity for list pagination and item fetch: **`entityId`** or **`eventId`** as declared on the item descriptor.
367
+ - Legacy `snapshotId` may be read as `recordId` fallback; do not write it in new code.
368
+
369
+ ### 4.2 Shipped entity ↔ database mapping
370
+
371
+ | Entity (`entityName`) | `target` | Typical collection(s) |
372
+ |-----------------------|----------|------------------------|
373
+ | `assets` | `entity` | `assets-snapshots` in `memorix-entities` |
374
+ | `variabilities-groups` | `entity` | `variabilities-groups-snapshots` |
375
+ | `vulnerabilities` | `event` | `vulnerabilities-snapshots` in `memorix-events` |
376
+
377
+ Ingestion and completion must populate paths referenced in entity descriptors. If a property path is missing, retrieval returns null/omits the field and may emit **`EXTENSION_MISSING`** issues for required content types.
378
+
379
+ ### 4.3 Relations in data
380
+
381
+ Relations are **not** stored as Catalox edges. They are resolved at read time by matching:
382
+
383
+ - `relation.source.path` on the source record
384
+ - `relation.target.path` on target entity records
385
+
386
+ Both sides must use the same **`contentType`** keys declared on the relation. Target field projection uses `targetFields` / `includeRelations.fields`.
387
+
388
+ ---
389
+
390
+ ## 5. Runtime behavior (retrieval)
391
+
392
+ All Memorix-consuming components should rely on these behaviors rather than reimplementing them. Public exports are listed in [DATA-TIER-CONTRACT.md](./DATA-TIER-CONTRACT.md).
393
+
394
+ **Host bootstrap:** prefer `createMemorixRetrievalStackFromEnv()` for Catalox + Mongo wiring; see [EXPLORER-HOST-APIS.md](./EXPLORER-HOST-APIS.md) for graph/raw-read helpers.
395
+
396
+ ### 5.1 Discovery
397
+
398
+ ```
399
+ discoverMemorixEntities(client)
400
+ → list all items in memorix-entity-descriptors
401
+ → validate each as MemorixEntityDescriptor
402
+ → return summaries: id, entityName, label, defaultListDescriptorId, defaultItemDescriptorId, target
403
+ ```
404
+
405
+ ### 5.2 Entity-default list and item
406
+
407
+ ```
408
+ fetchMemorixListForEntity(client, { entityName, ... })
409
+ → load entity descriptor
410
+ → listId = entity.defaultListDescriptorId
411
+ → fetchMemorixList(client, { listId, ... })
412
+
413
+ fetchMemorixItemForEntity(client, { entityName, entityId | eventId, ... })
414
+ → load entity descriptor
415
+ → itemDescriptorId = entity.defaultItemDescriptorId
416
+ → fetchMemorixItem(client, { itemDescriptorId, ... })
417
+ ```
418
+
419
+ ### 5.3 List composition
420
+
421
+ 1. Load list + entity descriptors from Catalox.
422
+ 2. Validate every `fields[]` entry exists and is `humanReadable`.
423
+ 3. Resolve pagination driver = `leadingContentType` (or sort override if allowed).
424
+ 4. Query driver collection with list default filters merged with request filters, plus optional `searchText` across human-readable fields.
425
+ 5. Optionally batch-fetch extensions and relations.
426
+ 6. Return rows as flat property maps + pagination + issues.
427
+
428
+ ### 5.4 Item composition
429
+
430
+ 1. Load item + entity descriptors.
431
+ 2. Fetch all documents per `contentTypes[]` matching identity.
432
+ 3. Apply `multiMatch` per content type.
433
+ 4. Build `sections[]` from property definitions.
434
+ 5. Resolve `includeRelations` and optional full content fetches.
435
+ 6. Return `{ identity, sections, relations?, issues? }`.
436
+
437
+ ### 5.5 Entity graph (Explorer)
438
+
439
+ `buildMemorixEntityGraph` uses discovery + entity descriptors only:
440
+
441
+ - Per entity: label, counts, content-type labels, **`relations` → connections**, default list/item ids.
442
+ - `discovery.source` is `"catalox"` or `"none"` — never `"env"`.
443
+
444
+ ---
445
+
446
+ ## 6. Seeding and keeping peers in sync
447
+
448
+ ### 6.1 Source of truth
449
+
450
+ | Artifact | Location |
451
+ |----------|----------|
452
+ | Editable seed inputs | `@x12i/memorix-retrieval` → `catalox-seeds/inputs/` |
453
+ | Built manifest | `catalox-seeds/memorix-retrieval-descriptors.manifest.json` |
454
+ | TypeScript types | `src/descriptors/descriptor-types.ts` |
455
+
456
+ Workflow for descriptor changes:
457
+
458
+ ```bash
459
+ # In memorix-retrieval
460
+ npm run catalox:seed:build
461
+ npm run catalox:seed:memorix-retrieval:validate
462
+ npm run catalox:seed:memorix-retrieval:apply # force apply
463
+ npm run catalox:seed:ensure # apply only if items missing
464
+ ```
465
+
466
+ Seed helpers (`buildMemorixRetrievalSeedManifest`, `checkMemorixRetrievalSeedNeeds`, …) are exported from the package; `scripts/` wrap them for CLI use. See [DATA-TIER-CONTRACT.md](./DATA-TIER-CONTRACT.md#catalox-seed-helpers-library).
467
+
468
+ Then bump `@x12i/memorix-retrieval` and reinstall in consuming apps.
469
+
470
+ ### 6.2 Adding a new entity (checklist)
471
+
472
+ 1. Add `catalox-seeds/inputs/entity-descriptors/<name>.json` with **both default list and item ids**.
473
+ 2. Add at least one list under `list-descriptors/` and one item under `item-descriptors/`.
474
+ 3. Register ids in `catalox-seeds/inputs/manifest.json`.
475
+ 4. Rebuild and apply seed manifest.
476
+ 5. Ensure Mongo collections exist and documents match declared paths.
477
+ 6. Extend `src/tests/fixtures.ts` and validation tests in retrieval.
478
+ 7. Publish retrieval; update Explorer (or other hosts) dependency.
479
+
480
+ ### 6.3 Versioning
481
+
482
+ - **Descriptor schema changes** → minor/major bump of `@x12i/memorix-retrieval` + coordinated seed apply.
483
+ - **Seed-only changes** (fields, filters, new list views) → seed apply + retrieval patch as needed; hosts pick up via graph/list/item APIs automatically.
484
+ - Do not fork catalog ids per app; use **`scope`** for domain/agent filtering instead.
485
+
486
+ ---
487
+
488
+ ## 7. Component matrix
489
+
490
+ | Component | Reads Catalox | Writes Catalox | Reads Mongo | Writes Mongo |
491
+ |-----------|---------------|----------------|-------------|--------------|
492
+ | `@x12i/memorix-retrieval` | Yes (3 catalogs) | No (seeds via CLI) | Yes (descriptor-driven) | No |
493
+ | `@x12i/memorix-completion` | No* | No | Yes (source + targets) | Yes (`data` on targets) |
494
+ | `@x12i/memorix-explorer` | Via retrieval | No | No (via retrieval only) | No |
495
+ | Ingestion pipelines | No* | No | Yes | Yes |
496
+
497
+ \*Completion and ingestion should **align** with descriptor paths and collection names documented here; they do not need Catalox at runtime unless they also ship seed tooling.
498
+
499
+ ---
500
+
501
+ ## 8. Forbidden patterns
502
+
503
+ These break cross-component sync and must not appear in new code:
504
+
505
+ | Pattern | Why |
506
+ |---------|-----|
507
+ | Host Mongo list/get/count by collection name | Bypasses list/item descriptors |
508
+ | `MEMORIX_ENTITY_NAMES` or env entity lists | Bypasses Catalox discovery |
509
+ | `@x12i/xmemory-store` / scoped tier in Explorer | Legacy; replaced by retrieval + descriptors |
510
+ | Parallel catalog types for the same semantics | Fragments truth |
511
+ | Credentials inside descriptor JSON | Use `credentialsRef` + host-injected readers |
512
+ | List fields with `humanReadable: false` | Validation error at fetch time |
513
+ | Entity descriptor missing `defaultListDescriptorId` / `defaultItemDescriptorId` | Breaks Option A wiring |
514
+
515
+ ---
516
+
517
+ ## 9. Quick reference — shipped ids
518
+
519
+ From `catalox-seeds/inputs/manifest.json`:
520
+
521
+ **Entities:** `assets`, `vulnerabilities`, `variabilities-groups`
522
+
523
+ **Lists:** `assets-main-list`, `vulnerabilities-main-list`, `critical-vulnerabilities-list`, `variabilities-groups-main-list`
524
+
525
+ **Items:** `asset-detail-item`, `vulnerability-detail-item`, `variabilities-group-detail-item`
526
+
527
+ **Scope (all seeded items):** `domains: [network, vulnerabilities]`, `agents: [neo]`
528
+
529
+ ---
530
+
531
+ ## 10. Related packages
532
+
533
+ | Package | Role |
534
+ |---------|------|
535
+ | `@x12i/catalox` | Catalog storage and seed CLI |
536
+ | `@x12i/memorix-retrieval` | Descriptor-driven data tier |
537
+ | `@x12i/memorix-completion` | Source → Memorix enrichment (payload paths must match descriptors) |
538
+ | `@x12i/memorix-explorer` | UI host — consumes retrieval APIs only |
539
+
540
+ When adding a capability all hosts need (e.g. cross-entity workspace list), **extend retrieval and descriptors first**, then update hosts — never shim in a single app.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/memorix-retrieval",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Descriptor-driven Memorix retrieval layer for lists, items, and content objects",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,9 +19,10 @@
19
19
  "scripts": {
20
20
  "build": "tsc",
21
21
  "test": "vitest run",
22
- "catalox:seed:build": "node scripts/build-seed-manifest.mjs",
22
+ "catalox:seed:build": "npm run build && node scripts/build-seed-manifest.mjs",
23
23
  "catalox:seed:memorix-retrieval:validate": "npm run catalox:seed:build && catalox seed validate --file catalox-seeds/memorix-retrieval-descriptors.manifest.json",
24
- "catalox:seed:memorix-retrieval:apply": "npm run catalox:seed:build && node scripts/apply-seed-manifest.mjs",
24
+ "catalox:seed:memorix-retrieval:apply": "npm run build && node scripts/apply-seed-manifest.mjs",
25
+ "catalox:seed:ensure": "npm run build && node scripts/ensure-catalox-seed.mjs",
25
26
  "mongo:check-collections": "node scripts/check-mongo-collections.mjs",
26
27
  "smoke:retrieval": "npm run build && node scripts/smoke-retrieval.mjs"
27
28
  },