@x12i/memorix-retrieval 1.5.1 → 1.6.2

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 (44) hide show
  1. package/dist/client/create-client.d.ts.map +1 -1
  2. package/dist/client/create-client.js +4 -4
  3. package/dist/client/create-client.js.map +1 -1
  4. package/dist/client/xronox-adapter.d.ts.map +1 -1
  5. package/dist/client/xronox-adapter.js +7 -0
  6. package/dist/client/xronox-adapter.js.map +1 -1
  7. package/dist/client/xronox-like.d.ts +22 -0
  8. package/dist/client/xronox-like.d.ts.map +1 -1
  9. package/dist/data/memorix-count.d.ts +1 -0
  10. package/dist/data/memorix-count.d.ts.map +1 -1
  11. package/dist/data/memorix-count.js +1 -0
  12. package/dist/data/memorix-count.js.map +1 -1
  13. package/dist/data/memorix-read.d.ts.map +1 -1
  14. package/dist/data/memorix-read.js +5 -0
  15. package/dist/data/memorix-read.js.map +1 -1
  16. package/dist/explorer/collection-inventory.d.ts +77 -0
  17. package/dist/explorer/collection-inventory.d.ts.map +1 -0
  18. package/dist/explorer/collection-inventory.js +302 -0
  19. package/dist/explorer/collection-inventory.js.map +1 -0
  20. package/dist/explorer/entity-graph.d.ts +35 -12
  21. package/dist/explorer/entity-graph.d.ts.map +1 -1
  22. package/dist/explorer/entity-graph.js +117 -55
  23. package/dist/explorer/entity-graph.js.map +1 -1
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/tests/collection-inventory.test.d.ts +2 -0
  29. package/dist/tests/collection-inventory.test.d.ts.map +1 -0
  30. package/dist/tests/collection-inventory.test.js +207 -0
  31. package/dist/tests/collection-inventory.test.js.map +1 -0
  32. package/dist/tests/entity-graph.test.d.ts +2 -0
  33. package/dist/tests/entity-graph.test.d.ts.map +1 -0
  34. package/dist/tests/entity-graph.test.js +148 -0
  35. package/dist/tests/entity-graph.test.js.map +1 -0
  36. package/docs/DATA-TIER-CONTRACT.md +6 -3
  37. package/docs/EXPLORER-HOST-APIS.md +16 -2
  38. package/docs/MEMORIX-CATALOX-CONTRACTS.md +8 -4
  39. package/docs/XRONOX-DATA-TIER-REQUIREMENTS.md +46 -349
  40. package/docs/fr/README.md +15 -0
  41. package/docs/fr/xronox-fr-list-collections.md +196 -0
  42. package/docs/fr/xronox-fr-memorix-env-preset.md +69 -0
  43. package/docs/fr/xronox-fr-per-role-selftest.md +60 -0
  44. package/package.json +2 -2
@@ -0,0 +1,207 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { createMemorixRetrieval } from "../client/create-client.js";
3
+ import { MEMORIX_ENTITY_DESCRIPTORS_CATALOG } from "../descriptors/catalog-ids.js";
4
+ import { buildMemorixCollectionInventory, orphanNodesFromInventory, parseOrphanCollection, } from "../explorer/collection-inventory.js";
5
+ import { assetsEntity, vulnerabilitiesEntity } from "./fixtures.js";
6
+ const variabilitiesGroupsEntity = {
7
+ id: "variabilities-groups",
8
+ entityName: "variabilities-groups",
9
+ defaultListDescriptorId: "variabilities-groups-main-list",
10
+ defaultItemDescriptorId: "variabilities-groups-detail-item",
11
+ target: "entity",
12
+ collectionPrefix: "variabilities-groups",
13
+ identity: {
14
+ allowedIdFields: ["entityId"],
15
+ requiredExactlyOne: true,
16
+ defaultIdField: "entityId",
17
+ },
18
+ defaults: {
19
+ canonicalContentType: "snapshots",
20
+ dataRoot: "data",
21
+ effectiveDatePath: "modifiedAt",
22
+ fallbackEffectiveDatePaths: ["capturedAt"],
23
+ },
24
+ contentTypes: {
25
+ snapshots: {
26
+ postfix: "snapshots",
27
+ collection: "variabilities-groups-snapshots",
28
+ dataRoot: "data",
29
+ isCanonical: true,
30
+ },
31
+ },
32
+ properties: {},
33
+ };
34
+ function mockInventoryCatalox() {
35
+ const entityItems = [
36
+ { id: "assets", data: assetsEntity, title: "Assets" },
37
+ { id: "vulnerabilities", data: vulnerabilitiesEntity, title: "Vulnerabilities" },
38
+ {
39
+ id: "variabilities-groups",
40
+ data: variabilitiesGroupsEntity,
41
+ title: "Variabilities Groups",
42
+ },
43
+ ];
44
+ return {
45
+ getCatalogItem: vi.fn(async (_ctx, catalogId, itemId) => {
46
+ const map = {
47
+ assets: assetsEntity,
48
+ vulnerabilities: vulnerabilitiesEntity,
49
+ "variabilities-groups": variabilitiesGroupsEntity,
50
+ };
51
+ if (catalogId === MEMORIX_ENTITY_DESCRIPTORS_CATALOG && map[itemId]) {
52
+ return { outcome: "found", item: { data: map[itemId] } };
53
+ }
54
+ return { outcome: "not_found" };
55
+ }),
56
+ listCatalogItems: vi.fn(async () => ({
57
+ items: entityItems,
58
+ hasMore: false,
59
+ })),
60
+ };
61
+ }
62
+ function mockInventoryXronox() {
63
+ const entityCollections = [
64
+ "assets-snapshots",
65
+ "assets-analysis",
66
+ "variabilities-groups-snapshots",
67
+ "orphan-entity-records",
68
+ ];
69
+ const eventCollections = ["vulnerabilities-snapshots", "unknown-event-bucket"];
70
+ const counts = {
71
+ "assets-snapshots": 5,
72
+ "assets-analysis": 0,
73
+ "vulnerabilities-snapshots": 2,
74
+ "variabilities-groups-snapshots": 0,
75
+ "orphan-entity-records": 12,
76
+ "unknown-event-bucket": 0,
77
+ };
78
+ return {
79
+ listCollections: vi.fn(async (params) => {
80
+ if (params.role === "memorix_events") {
81
+ return {
82
+ database: "memorix-events",
83
+ collections: eventCollections.map((name) => ({ name })),
84
+ };
85
+ }
86
+ return {
87
+ database: "memorix-entities",
88
+ collections: entityCollections.map((name) => ({ name })),
89
+ };
90
+ }),
91
+ countDocuments: vi.fn(async (params) => {
92
+ void params.estimated;
93
+ return counts[params.source] ?? 0;
94
+ }),
95
+ read: vi.fn(async () => []),
96
+ };
97
+ }
98
+ describe("parseOrphanCollection", () => {
99
+ it("matches longest known prefix for hyphenated entity names", () => {
100
+ const prefixes = ["assets", "variabilities-groups", "vulnerabilities"].sort((a, b) => b.length - a.length);
101
+ expect(parseOrphanCollection("variabilities-groups-snapshots", prefixes)).toEqual({
102
+ entityName: "variabilities-groups",
103
+ contentType: "snapshots",
104
+ confidence: "heuristic",
105
+ });
106
+ });
107
+ it("falls back to last-hyphen split when no prefix matches", () => {
108
+ expect(parseOrphanCollection("custom-orphan-data", ["assets"])).toEqual({
109
+ entityName: "custom-orphan",
110
+ contentType: "data",
111
+ confidence: "heuristic",
112
+ });
113
+ });
114
+ });
115
+ describe("buildMemorixCollectionInventory", () => {
116
+ it("throws when xronox.listCollections is not available", async () => {
117
+ const client = createMemorixRetrieval({
118
+ catalox: mockInventoryCatalox(),
119
+ xronox: {
120
+ countDocuments: vi.fn(async () => 0),
121
+ read: vi.fn(async () => []),
122
+ },
123
+ processEnv: {
124
+ MEMORIX_ENTITIES_DB: "memorix-entities",
125
+ MEMORIX_EVENTS_DB: "memorix-events",
126
+ },
127
+ });
128
+ await expect(buildMemorixCollectionInventory(client)).rejects.toMatchObject({
129
+ code: "XRONOX_LIST_COLLECTIONS_UNSUPPORTED",
130
+ });
131
+ });
132
+ it("reconciles declared slots and orphan collections across both databases via Xronox", async () => {
133
+ const client = createMemorixRetrieval({
134
+ catalox: mockInventoryCatalox(),
135
+ xronox: mockInventoryXronox(),
136
+ processEnv: {
137
+ MEMORIX_ENTITIES_DB: "memorix-entities",
138
+ MEMORIX_EVENTS_DB: "memorix-events",
139
+ },
140
+ });
141
+ const inventory = await buildMemorixCollectionInventory(client);
142
+ expect(inventory.memorixDatabases).toEqual({
143
+ entity: "memorix-entities",
144
+ event: "memorix-events",
145
+ });
146
+ const assetsSnapshots = inventory.entries.find((e) => e.collection === "assets-snapshots");
147
+ expect(assetsSnapshots).toMatchObject({
148
+ matchStatus: "matched",
149
+ matchConfidence: "descriptor",
150
+ target: "entity",
151
+ listable: true,
152
+ });
153
+ const assetsAnalysis = inventory.entries.find((e) => e.collection === "assets-analysis");
154
+ expect(assetsAnalysis).toMatchObject({
155
+ matchStatus: "empty",
156
+ documentCount: 0,
157
+ });
158
+ const orphan = inventory.entries.find((e) => e.collection === "orphan-entity-records");
159
+ expect(orphan).toMatchObject({
160
+ matchStatus: "orphan",
161
+ entityName: "orphan-entity",
162
+ contentType: "records",
163
+ target: "entity",
164
+ });
165
+ expect(inventory.summary.byTarget.entity.matchedPopulated).toBe(inventory.entries.filter((e) => e.target === "entity" && e.matchStatus === "matched").length);
166
+ expect(inventory.summary.byTarget.event.matchedPopulated).toBe(inventory.entries.filter((e) => e.target === "event" && e.matchStatus === "matched").length);
167
+ });
168
+ });
169
+ describe("orphanNodesFromInventory", () => {
170
+ it("groups orphan entries by target and entity name", () => {
171
+ const nodes = orphanNodesFromInventory([
172
+ {
173
+ collection: "orphan-entity-records",
174
+ database: "memorix-entities",
175
+ target: "entity",
176
+ documentCount: 12,
177
+ entityName: "orphan-entity",
178
+ contentType: "records",
179
+ matchStatus: "orphan",
180
+ matchConfidence: "heuristic",
181
+ listable: false,
182
+ },
183
+ {
184
+ collection: "orphan-entity-notes",
185
+ database: "memorix-entities",
186
+ target: "entity",
187
+ documentCount: 3,
188
+ entityName: "orphan-entity",
189
+ contentType: "notes",
190
+ matchStatus: "orphan",
191
+ matchConfidence: "heuristic",
192
+ listable: false,
193
+ },
194
+ ]);
195
+ expect(nodes).toHaveLength(1);
196
+ expect(nodes[0]).toMatchObject({
197
+ entityName: "orphan-entity",
198
+ inventoryStatus: "orphan",
199
+ totalDocuments: 15,
200
+ contentTypesWithData: 2,
201
+ listable: false,
202
+ drillable: false,
203
+ });
204
+ expect(nodes[0]?.contentTypes).toHaveLength(2);
205
+ });
206
+ });
207
+ //# sourceMappingURL=collection-inventory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-inventory.test.js","sourceRoot":"","sources":["../../src/tests/collection-inventory.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=entity-graph.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-graph.test.d.ts","sourceRoot":"","sources":["../../src/tests/entity-graph.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,148 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { createMemorixRetrieval } from "../client/create-client.js";
3
+ import { MEMORIX_ENTITY_DESCRIPTORS_CATALOG } from "../descriptors/catalog-ids.js";
4
+ import { buildMemorixEntityGraph, buildMemorixEntitySlices, } from "../explorer/entity-graph.js";
5
+ import { assetsEntity, vulnerabilitiesEntity, } from "./fixtures.js";
6
+ function mockEntityCatalox() {
7
+ const entityItems = [
8
+ { id: "assets", data: assetsEntity, title: "Assets" },
9
+ { id: "vulnerabilities", data: vulnerabilitiesEntity, title: "Vulnerabilities" },
10
+ ];
11
+ return {
12
+ getCatalogItem: vi.fn(async (_ctx, catalogId, itemId) => {
13
+ if (catalogId === MEMORIX_ENTITY_DESCRIPTORS_CATALOG && itemId === "assets") {
14
+ return { outcome: "found", item: { data: assetsEntity } };
15
+ }
16
+ if (catalogId === MEMORIX_ENTITY_DESCRIPTORS_CATALOG &&
17
+ itemId === "vulnerabilities") {
18
+ return {
19
+ outcome: "found",
20
+ item: { data: vulnerabilitiesEntity },
21
+ };
22
+ }
23
+ return { outcome: "not_found" };
24
+ }),
25
+ listCatalogItems: vi.fn(async () => ({
26
+ items: entityItems,
27
+ hasMore: false,
28
+ })),
29
+ };
30
+ }
31
+ function mockXronox(counts) {
32
+ return {
33
+ countDocuments: vi.fn(async (params) => counts[params.source] ?? 0),
34
+ read: vi.fn(async () => []),
35
+ };
36
+ }
37
+ describe("buildMemorixEntityGraph", () => {
38
+ it("includes target, memorixDatabase, contentTypes, and memorixDatabases", async () => {
39
+ const client = createMemorixRetrieval({
40
+ catalox: mockEntityCatalox(),
41
+ xronox: mockXronox({
42
+ "assets-snapshots": 3,
43
+ "assets-analysis": 0,
44
+ "vulnerabilities-snapshots": 7,
45
+ }),
46
+ processEnv: {
47
+ MEMORIX_ENTITIES_DB: "memorix-entities",
48
+ MEMORIX_EVENTS_DB: "memorix-events",
49
+ },
50
+ });
51
+ const graph = await buildMemorixEntityGraph(client);
52
+ expect(graph.discovery.memorixDatabases).toEqual({
53
+ entity: "memorix-entities",
54
+ event: "memorix-events",
55
+ });
56
+ expect(graph.memorixDb).toBe("memorix-entities + memorix-events");
57
+ const assets = graph.entities.find((e) => e.entityName === "assets");
58
+ expect(assets).toMatchObject({
59
+ target: "entity",
60
+ memorixDatabase: "memorix-entities",
61
+ count: 3,
62
+ listable: true,
63
+ });
64
+ expect(assets?.contentTypes).toEqual([
65
+ expect.objectContaining({
66
+ key: "snapshots",
67
+ collection: "assets-snapshots",
68
+ database: "memorix-entities",
69
+ target: "entity",
70
+ count: 3,
71
+ status: "populated",
72
+ }),
73
+ expect.objectContaining({
74
+ key: "analysis",
75
+ collection: "assets-analysis",
76
+ count: 0,
77
+ status: "empty",
78
+ }),
79
+ ]);
80
+ const vulns = graph.entities.find((e) => e.entityName === "vulnerabilities");
81
+ expect(vulns).toMatchObject({
82
+ target: "event",
83
+ memorixDatabase: "memorix-events",
84
+ count: 7,
85
+ });
86
+ });
87
+ it("uses content-type keys for countErrors", async () => {
88
+ const client = createMemorixRetrieval({
89
+ catalox: mockEntityCatalox(),
90
+ xronox: {
91
+ countDocuments: vi.fn(async (params) => {
92
+ if (params.source === "assets-analysis") {
93
+ throw new Error("count failed");
94
+ }
95
+ return params.source === "assets-snapshots" ? 1 : 0;
96
+ }),
97
+ read: vi.fn(async () => []),
98
+ },
99
+ processEnv: {
100
+ MEMORIX_ENTITIES_DB: "memorix-entities",
101
+ MEMORIX_EVENTS_DB: "memorix-events",
102
+ },
103
+ });
104
+ const graph = await buildMemorixEntityGraph(client);
105
+ const assets = graph.entities.find((e) => e.entityName === "assets");
106
+ expect(assets?.countErrors).toEqual({ analysis: "count failed" });
107
+ expect(assets?.contentTypes.find((c) => c.key === "analysis")).toMatchObject({
108
+ status: "error",
109
+ error: "count failed",
110
+ });
111
+ });
112
+ });
113
+ describe("buildMemorixEntitySlices", () => {
114
+ it("includes all content types including zero counts", async () => {
115
+ const client = createMemorixRetrieval({
116
+ catalox: mockEntityCatalox(),
117
+ xronox: mockXronox({
118
+ "assets-snapshots": 2,
119
+ "assets-analysis": 0,
120
+ }),
121
+ processEnv: {
122
+ MEMORIX_ENTITIES_DB: "memorix-entities",
123
+ MEMORIX_EVENTS_DB: "memorix-events",
124
+ },
125
+ });
126
+ const result = await buildMemorixEntitySlices(client, "assets");
127
+ expect(result).toMatchObject({
128
+ entityName: "assets",
129
+ target: "entity",
130
+ memorixDatabase: "memorix-entities",
131
+ });
132
+ expect(result.slices).toHaveLength(2);
133
+ expect(result.slices[0]).toMatchObject({
134
+ contentType: "snapshots",
135
+ collection: "assets-snapshots",
136
+ target: "entity",
137
+ count: 2,
138
+ status: "populated",
139
+ });
140
+ expect(result.slices[1]).toMatchObject({
141
+ contentType: "analysis",
142
+ collection: "assets-analysis",
143
+ count: 0,
144
+ status: "empty",
145
+ });
146
+ });
147
+ });
148
+ //# sourceMappingURL=entity-graph.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-graph.test.js","sourceRoot":"","sources":["../../src/tests/entity-graph.test.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@ See also:
10
10
  - [MEMORIX-DATABASE-CONVENTIONS.md](./MEMORIX-DATABASE-CONVENTIONS.md) — Mongo layout and env vars
11
11
  - [EXPLORER-HOST-APIS.md](./EXPLORER-HOST-APIS.md) — graph, raw reads, scoped workspace (Explorer-oriented)
12
12
  - [XRONOX-DATA-TIER-REQUIREMENTS.md](./XRONOX-DATA-TIER-REQUIREMENTS.md) — what `@x12i/xronox` must provide as Memorix data tier
13
+ - [fr/README.md](./fr/README.md) — **open feature requests for `@x12i/xronox`** (implement there first)
13
14
 
14
15
  ---
15
16
 
@@ -66,8 +67,10 @@ List descriptor `filters` with `operator` + `value` are **always applied**; requ
66
67
 
67
68
  | Export | Purpose |
68
69
  |--------|---------|
69
- | `buildMemorixEntityGraph(client, options?)` | Discovery + counts + relation graph |
70
- | `buildMemorixEntitySlices(client, entityName)` | Per–content-type counts for drill-down |
70
+ | `buildMemorixEntityGraph(client, options?)` | Discovery + counts + relation graph (per–content-type metadata, both DBs) |
71
+ | `buildMemorixEntitySlices(client, entityName)` | Per–content-type counts for drill-down (includes zero counts) |
72
+ | `buildMemorixCollectionInventory(client, options?)` | Reconcile Catalox-declared collections with Mongo in both databases via Xronox `listCollections` (≥ 3.9.0) |
73
+ | `orphanNodesFromInventory(entries)` | Group inventory orphans for entity graph canvas |
71
74
  | `getMemorixRetrievalHealth(client)` | Mongo ping + discovery sample |
72
75
  | `countMemorixEntityContentTypeDocuments` | Count by entity + content type (descriptor-resolved collection) |
73
76
  | `listMemorixEntityContentTypeDocuments` | Paginated raw documents (descriptor-resolved) |
@@ -117,7 +120,7 @@ Scripts (not imported by hosts at runtime):
117
120
  |--------|---------|
118
121
  | `memorixRead` | Routed read via Xronox (entity context + collection + role) |
119
122
  | `memorixCount` | Routed count via Xronox (`countDocuments` on adapter) |
120
- | `readMemorixCollection` / `countMemorixCollection` / `connectMemorixMongo` | **Advanced** — direct Mongo when injecting `mongo` client |
123
+ | `readMemorixCollection` / `countMemorixCollection` / `connectMemorixMongo` | **Test-only** — not used by production inventory or list/item paths |
121
124
  | `resolveMemorixCollectionName` / `resolveMemorixDbNameForEntity` | Resolution utilities |
122
125
  | Env helpers: `resolveMemorixDbName`, `resolveMongoUri`, `targetCollectionEnvKey` | Advanced overrides |
123
126
 
@@ -19,8 +19,22 @@ Uses `createCataloxFromEnv` + `createCataloxAdapterFromBound` + `createMemorixRe
19
19
  ## Entity discovery
20
20
 
21
21
  - `discoverMemorixEntities(client)` — lists `memorix-entity-descriptors` via Catalox only (`source: "catalox" | "none"`). No env entity list fallback.
22
- - `buildMemorixEntityGraph(client)` — discovery + per–content-type counts + **relations from entity descriptors**.
23
- - `buildMemorixEntitySlices(client, entityName)` — slice counts for canvas drill-down.
22
+ - `buildMemorixEntityGraph(client)` — discovery + per–content-type counts + **relations from entity descriptors**. Each entity includes `target`, `memorixDatabase`, and `contentTypes[]` (collection, database, count, status). `discovery.memorixDatabases` names both entity and event stores.
23
+ - `buildMemorixEntitySlices(client, entityName)` — slice counts for canvas drill-down; includes **all** declared content types (zero counts use `status: "empty"`).
24
+ - `buildMemorixCollectionInventory(client, options?)` — bidirectional Mongo ↔ Catalox inventory across both databases (`matched`, `empty`, `orphan`, `unresolved`). Options: `includeExactCounts`, `scopedNamespace`.
25
+ - `orphanNodesFromInventory(entries)` — group orphan inventory rows into graph nodes for Explorer entities canvas.
26
+
27
+ ## Collection inventory
28
+
29
+ Requires `@x12i/xronox` ≥ 3.9.0 (`listCollections`).
30
+
31
+ - Declared counts: `countMemorixEntityContentTypeDocuments` (Xronox)
32
+ - DB scan: `xronox.listCollections({ role })` for `memorix_entities` and `memorix_events`
33
+ - Orphan counts: `xronox.countDocuments({ estimated: true })`
34
+
35
+ Smoke: `npm run smoke:retrieval -- --inventory`
36
+
37
+ Response: `entries`, `summary.byTarget`, `discovery`.
24
38
 
25
39
  ## Raw collection reads (Explorer compatibility)
26
40
 
@@ -481,12 +481,16 @@ When `discovery.source` is `"none"`, returns empty rows with a hint — no env e
481
481
  5. Resolve `includeRelations` and optional full content fetches.
482
482
  6. Return `{ identity, sections, relations?, issues? }`.
483
483
 
484
- ### 5.6 Entity graph (Explorer)
484
+ ### 5.6 Entity graph and collection inventory (Explorer)
485
485
 
486
- `buildMemorixEntityGraph` uses discovery + entity descriptors only:
486
+ Retrieval exports (see [EXPLORER-HOST-APIS.md](./EXPLORER-HOST-APIS.md)):
487
487
 
488
- - Per entity: label, counts, content-type labels, **`relations` → connections**, default list/item ids.
489
- - `discovery.source` is `"catalox"` or `"none"` never `"env"`.
488
+ - `buildMemorixEntityGraph` — discovery + entity descriptors; per entity: label, counts, **`contentTypes[]`**, **`relations` → connections**, default list/item ids, `target`, `memorixDatabase`.
489
+ - `buildMemorixEntitySlices` all declared content types including zero counts.
490
+ - `buildMemorixCollectionInventory` — reconcile declared collections with Mongo in both databases.
491
+ - `orphanNodesFromInventory` — orphan groups for entities canvas.
492
+
493
+ `discovery.source` is `"catalox"` or `"none"` — never `"env"`. `discovery.memorixDatabases` names both entity and event stores.
490
494
 
491
495
  ---
492
496