@x12i/memorix-metadata 3.6.2 → 3.6.3

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 3.6.3
2
+
3
+ - Updated `docs/memorix-format-reference.md` (copied from `@x12i/memorix-format` at pack time). It now shows how a content type and its `profile` become the `ContentTypeProfileV2` that record validation uses, and how to validate a record against a pack offline. No validation change.
4
+
1
5
  ## 3.6.2
2
6
 
3
7
  - Ship `docs/memorix-format-reference.md` and `CHANGELOG.md`. Object types, content types, and descriptors were already this package; the reference was not. No validation change.
package/README.md CHANGED
@@ -9,7 +9,7 @@ Install it on its own. The only runtime dependency is `@x12i/memorix-mapping`. R
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- npm install @x12i/memorix-metadata@3.6.2
12
+ npm install @x12i/memorix-metadata@3.6.3
13
13
  ```
14
14
 
15
15
  | Audience | Use this package? |
@@ -83,5 +83,6 @@ Abstracts stay **virtual** — no abstract collections and no abstract `recordId
83
83
 
84
84
  ## Docs
85
85
 
86
- - [`docs/memorix-format-reference.md`](./docs/memorix-format-reference.md) — records, object types, content types, descriptors, and mappings. The same file ships in `@x12i/memorix-format`.
86
+ - [`docs/memorix-format-reference.md`](./docs/memorix-format-reference.md) — records, object types, content types, descriptors, and mappings. Section 5 covers object types, content types, and the descriptor; section 5.7 covers validating a record against a pack.
87
+ - [`examples/`](./examples/) — valid packs, and `examples/invalid/` with one failing rule each.
87
88
  - Hub: [https://docs.memorix.x12i.com](https://docs.memorix.x12i.com)
@@ -6,15 +6,16 @@ This document puts in one place everything that defines how Memorix data is shap
6
6
  2. The **metadata pack** (`memorix-metadata/1`): where an agent declares its **object types (OTs)**, their **content types (CTs)**, the CT **descriptor** (`profile`), fields, abstracts, and relationships.
7
7
  3. **Mappings** (the mapping AST): how provider payloads land on an OT/CT, and how concrete fields project to abstract properties.
8
8
 
9
- Each section names the source file that is the law for it. When this document and the code disagree, the code wins.
9
+ Each section names the package and export that is the law for it. When this document and the code disagree, the code wins.
10
10
 
11
11
  ## Using these packages on their own
12
12
 
13
13
  Memorix depends on these packages. Anyone else can install them without the service, the database layer, or the pipeline runtime.
14
14
 
15
15
  ```bash
16
- npm install @x12i/memorix-format
17
- npm install @x12i/memorix-metadata
16
+ npm install @x12i/memorix-format # records
17
+ npm install @x12i/memorix-metadata # object types, content types, descriptors (brings @x12i/memorix-mapping)
18
+ npm install @x12i/memorix-mapping # mappings only
18
19
  ```
19
20
 
20
21
  | Package | What you get | Runtime dependencies |
@@ -25,7 +26,7 @@ npm install @x12i/memorix-metadata
25
26
 
26
27
  `@x12i/memorix-metadata` installs `@x12i/memorix-mapping` for you. Validating a record or a pack does not require `@x12i/memorix-data`, `@x12i/memorix-service`, or Mongo. Routing and the write path live in `@x12i/memorix-data`; they are listed below so the error codes make sense, and they are not needed to use the format or the descriptors.
27
28
 
28
- This file ships as `docs/memorix-format-reference.md` in both `@x12i/memorix-format` and `@x12i/memorix-metadata`.
29
+ This file ships as `docs/memorix-format-reference.md` in all three packages. The copy in `@x12i/memorix-format` is the source; the other two are copied from it when they are packed.
29
30
 
30
31
  ```ts
31
32
  import { validateMemorixRecordV2 } from "@x12i/memorix-format/v2";
@@ -36,14 +37,14 @@ import {
36
37
  } from "@x12i/memorix-metadata";
37
38
  ```
38
39
 
39
- | Layer | Package | Source of truth |
40
+ | Layer | Package | Exports and files |
40
41
  |---|---|---|
41
- | Record envelope | `@x12i/memorix-format` | `memorix-format/docs/memorix-record-v2.md`, `memorix-format/src/v2/` |
42
- | OT / CT / descriptor / abstracts / relationships / mapping declarations | `@x12i/memorix-metadata` | `memorix-metadata/src/types.ts`, `memorix-metadata/src/validate.ts` |
43
- | Platform interfaces (Entity / Event / Knowledge) | `@x12i/memorix-metadata` | `memorix-metadata/src/platform-interfaces.ts` |
44
- | Mapping AST and execution | `@x12i/memorix-mapping` | `memorix-mapping/src/types.ts` |
45
- | Physical routing (database / collection names) | `@x12i/memorix-data` | `memorix-data/src/routing.ts` |
46
- | Write-time enforcement of the descriptor | `@x12i/memorix-data` | `memorix-data/src/write/record.ts` |
42
+ | Record envelope | `@x12i/memorix-format` | `MemorixRecordV2`, `validateMemorixRecordV2` (from `@x12i/memorix-format/v2`); `docs/memorix-record-v2.md` |
43
+ | Format-level content-type profile | `@x12i/memorix-format` | `ContentTypeProfileV2`, `BUILTIN_CONTENT_PROFILES`, `resolveContentProfile` |
44
+ | OT / CT / descriptor / abstracts / relationships / mapping declarations | `@x12i/memorix-metadata` | `MetadataPack`, `ObjectTypeDefinition`, `ContentTypeSpec`, `ContentTypeProfileDecl`, `validateMetadataPack` |
45
+ | Platform interfaces (Entity / Event / Knowledge) | `@x12i/memorix-metadata` | `PLATFORM_INTERFACES` |
46
+ | Mapping AST and execution | `@x12i/memorix-mapping` | `MappingDocument`, `MappingExpr`, `executeMapping`, `validateMappingDocument` |
47
+ | Physical routing and write-time checks | `@x12i/memorix-data` (Memorix runtime; not needed to use the format) | `databaseNameFor`, `contentCollectionName`, the content writer |
47
48
 
48
49
  ---
49
50
 
@@ -59,7 +60,7 @@ import {
59
60
  | **Abstract type** | A metadata-only type that one or more OTs project into (for example `People`). Never has a collection or its own `recordId`. Views, agent services, and relationships speak abstracts. |
60
61
  | **Platform interface** | The sealed abstracts `Entity`, `Event`, `Knowledge`. Every OT implements the one that matches its data category. |
61
62
  | **Mapping** | A declarative transform written in the mapping AST. Purpose is `source`, `abstract`, or `completion`. |
62
- | **Managed descriptor (legacy)** | Older Catalox "entity descriptors" in `memorix-retrieval/catalox-seeds`. Only an inventory of them is kept here (`memorix-format/meta/inventory/managed-descriptors.json`). They are not format law and not agent packs. |
63
+ | **Managed descriptor (legacy)** | Older Catalox "entity descriptors" in `memorix-retrieval/catalox-seeds`. Only an inventory of them is kept, in `meta/inventory/managed-descriptors.json` of `@x12i/memorix-format`. They are not format law and not agent packs. |
63
64
 
64
65
  ---
65
66
 
@@ -99,8 +100,10 @@ type MemorixRecordV2 = {
99
100
  };
100
101
  };
101
102
  data: Record<string, unknown>;
103
+ subject?: { objectType: string; recordId: string };
102
104
  context?: Record<string, unknown>;
103
105
  _metadata?: Record<string, unknown>;
106
+ _markdown?: { content: string; source: "ai" | "json" | "content"; metadata?: Record<string, unknown> };
104
107
  _audit?: Record<string, unknown>;
105
108
  _history?: Array<Record<string, unknown>>;
106
109
  analytics?: Record<string, unknown>;
@@ -112,8 +115,14 @@ type MemorixRecordV2 = {
112
115
  createdAt: string;
113
116
  modifiedAt: string;
114
117
  observedAt?: string;
118
+ writtenAt?: string;
115
119
  lifecycle?: Array<{ process: string; processedAt: string }>;
116
120
  provenance?: Record<string, unknown>;
121
+ _datasource?: {
122
+ service: string; ingress: string; basis: string; recordedAt: string;
123
+ origin?: string; channel?: string; originRecordId?: string; originAt?: string;
124
+ sources?: Array<{ system: string; name: string }>;
125
+ };
117
126
  deletion?: { deletedAt: string; by: string; via: "content" | "patch"; patchId?: string; reason?: string };
118
127
  };
119
128
  };
@@ -133,8 +142,10 @@ type MemorixRecordV2 = {
133
142
  | `concept.identifiers.primary` | yes | `{ kind, value }`, optional `issuer` and `normalizedValue`. |
134
143
  | `concept.identifiers.alternates` | no | Omit when empty. |
135
144
  | `data` | yes | The subject content itself. JSON markdown is rendered from `data` only. |
145
+ | `subject` | no | `{ objectType, recordId }` of the subject this row is about, when that subject is a different OT. The row keeps its own `recordId`. List filters use `subject.recordId`. |
136
146
  | `context` | no | Context payloads that are not the subject content. Omit when empty. Not rendered to markdown. |
137
147
  | `_metadata` | no | Producer context (for example `stepId`, `writtenBy`). A key here must not also be in `data`. Not `_system.provenance` and not `_markdown.metadata`. |
148
+ | `_markdown` | no | Original text beside `data`. `source` is `ai`, `json`, or `content`. Writers fill `source: "json"` from `data` when it is omitted. Readers omit it unless the fetch asks for markdown. |
138
149
  | `_audit` | no | Audit of the record. When present, `provider`, `model`, and `reasoningEffort` must be non-empty strings. Other keys are open. |
139
150
  | `_history` | no | Earlier products of this record, oldest first. Each entry is an object stored as given. An empty array is valid and differs from an absent key. A write that removes fields appends `{ action: "patch", caller, modifiedAt, removed: string[] }`. |
140
151
  | `analytics` | no | Numeric rollups. Allowed only when the CT descriptor has `allowSnapshotBuckets: true`. |
@@ -144,6 +155,8 @@ type MemorixRecordV2 = {
144
155
  | `_system.state` | yes | `active`, `archived`, `superseded`, or `deleted`. |
145
156
  | `_system.createdAt` / `modifiedAt` | yes | ISO timestamps. |
146
157
  | `_system.observedAt` | no | Default source for the `Event.occurredAt` interface property. |
158
+ | `_system.writtenAt` | no | Clock time of the write that produced this revision. |
159
+ | `_system._datasource` | no | Memorix-owned document source (`service`, `ingress`, `basis`, `recordedAt`, optional `origin`, `channel`, `sources`). Not part of `data`. |
147
160
  | `_system.lifecycle` | no | Appended only for meaningful process events. Omit when empty. Independent of `revision`. |
148
161
  | `_system.provenance` | no | Source attribution such as `sourceId` or `endpoint`. |
149
162
  | `_system.deletion` | conditional | Present if and only if `state` is `deleted` (`V2-DELETION`). |
@@ -154,8 +167,9 @@ type MemorixRecordV2 = {
154
167
  - `analytics`, `enrichment`, and `insights` appear only on CTs whose descriptor sets `allowSnapshotBuckets: true`.
155
168
  - If the CT declares a discriminator with a `defaultValue` and the record lacks it, validation warns `V2-DISCRIMINATOR-DEFAULT` and the writer fills the default. If it is required with no default, validation fails with `V2-DISCRIMINATOR`.
156
169
  - What a delete does is declared on the CT (`profile.deletion`), see section 5.2.
170
+ - Section 5.7 shows how the metadata descriptor becomes the profile that `validateMemorixRecordV2` checks.
157
171
 
158
- Golden examples live in `memorix-format/examples/v2/`.
172
+ Golden examples live in `examples/v2/` of `@x12i/memorix-format`.
159
173
 
160
174
  ---
161
175
 
@@ -245,7 +259,7 @@ type ContentTypeProfileDecl = {
245
259
  | `deletion.mode` | `logical`: the row stays with `_system.state: "deleted"` and reads hide it. `tracked`: the row is removed and a `memorix-deletion/1` entry is written to `deletions`. `untracked`: the row is removed and nothing is recorded. Omitted `deletion` means `{ mode: "tracked", keepBody: false }`. |
246
260
  | `deletion.keepBody` | Only valid with `tracked`. Keeps the removed body on the ledger entry. Purges and legacy-instance deletions never keep the body. A CT declares the same policy on every OT that uses it. |
247
261
 
248
- Write-time enforcement: if the effective catalog has no descriptor for the CT, the writer throws `METADATA_DESCRIPTOR_MISSING` (HTTP 400, with a `next` hint pointing at `/api/metadata/ensure`). See `memorix-data/src/write/record.ts`.
262
+ Write-time enforcement: if the effective catalog has no descriptor for the CT, the writer throws `METADATA_DESCRIPTOR_MISSING` (HTTP 400, with a `next` hint pointing at `/api/metadata/ensure`).
249
263
 
250
264
  ### 5.3 Fields (`ObjectTypeFieldSpec`)
251
265
 
@@ -275,7 +289,7 @@ Published by the skill API and run with `POST /api/v1/agent-functions/agents/{ag
275
289
 
276
290
  ### 5.5 Content-type vocabulary in use
277
291
 
278
- Any routing-safe id may be declared. These are the CTs the platform and current packs use (`memorix-format/meta/content-types.json`):
292
+ Any routing-safe id may be declared. These are the CTs the platform and current packs use (`meta/content-types.json` in `@x12i/memorix-format`):
279
293
 
280
294
  | CT | Typical descriptor | Purpose |
281
295
  |---|---|---|
@@ -293,6 +307,80 @@ Any routing-safe id may be declared. These are the CTs the platform and current
293
307
 
294
308
  The lander declares OTs and their source CTs. A skill appends only its target CTs to an OT that already exists, and never re-declares source CTs. Because ensure replaces the catalog, a skill always merges into the full pack from `/api/metadata/raw` before it ensures.
295
309
 
310
+ ### 5.7 From metadata descriptor to format profile
311
+
312
+ `@x12i/memorix-format` does not read metadata packs. `validateMemorixRecordV2` takes a `ContentTypeProfileV2`, the format-level descriptor:
313
+
314
+ ```ts
315
+ type ContentTypeProfileV2 = {
316
+ contentType: string;
317
+ allowSnapshotBuckets: boolean;
318
+ discriminator?: { path: string; required: boolean; defaultValue?: string; allowedValues?: string[] };
319
+ allowedExtraRoots?: string[];
320
+ deletion?: { mode: "logical" | "tracked" | "untracked"; keepBody?: boolean };
321
+ };
322
+ ```
323
+
324
+ Memorix builds it from the metadata content type like this:
325
+
326
+ | `ContentTypeProfileV2` | Comes from (`ContentTypeSpec`) |
327
+ |---|---|
328
+ | `contentType` | `id` |
329
+ | `allowSnapshotBuckets` | `profile.allowSnapshotBuckets` |
330
+ | `deletion` | `profile.deletion`, parsed with `parseDeletionPolicy`. An invalid declaration reads as absent. |
331
+ | `discriminator.path` | `discriminator.path`. With no path, there is no discriminator. |
332
+ | `discriminator.required` | `discriminator.required === true` (default `false`) |
333
+ | `discriminator.defaultValue` | `discriminator.defaultValue` when it is a non-empty string |
334
+ | `allowedValues`, `allowedExtraRoots` | Not set from metadata. Available to callers that validate offline. |
335
+
336
+ The lookup is by content-type id across the caller's effective object types, and the first match wins. That is why one CT id must declare the same `allowSnapshotBuckets` and `deletion` on every OT (`META-CT-PROFILE`).
337
+
338
+ When no profile is passed, `validateMemorixRecordV2` falls back to `resolveContentProfile(contentType)`, which uses `BUILTIN_CONTENT_PROFILES`:
339
+
340
+ | CT | `allowSnapshotBuckets` | Built-in discriminator |
341
+ |---|---|---|
342
+ | `snapshots`, `knowledge` | `true` | none |
343
+ | `raw`, `workflow`, `context`, `relationships`, `events` | `false` | none |
344
+ | `discovery` | `false` | `data.discoveryType`, default `default` |
345
+ | `analysis` | `false` | `data.analysisType`, default `default` |
346
+ | `decisions` | `false` | `data.decisionType`, default `default` |
347
+ | any other id | `false` | none |
348
+
349
+ A pack's descriptor always replaces the built-in profile, including its discriminator.
350
+
351
+ To validate a record offline against a pack, without Memorix:
352
+
353
+ ```ts
354
+ import {
355
+ validateMemorixRecordV2,
356
+ parseDeletionPolicy,
357
+ type ContentTypeProfileV2,
358
+ } from "@x12i/memorix-format/v2";
359
+ import type { ContentTypeSpec } from "@x12i/memorix-metadata";
360
+
361
+ function toProfile(ct: ContentTypeSpec): ContentTypeProfileV2 {
362
+ const deletion = ct.profile.deletion ? parseDeletionPolicy(ct.profile.deletion) : undefined;
363
+ return {
364
+ contentType: ct.id,
365
+ allowSnapshotBuckets: ct.profile.allowSnapshotBuckets,
366
+ ...(deletion?.ok ? { deletion: deletion.policy } : {}),
367
+ ...(ct.discriminator?.path
368
+ ? {
369
+ discriminator: {
370
+ path: ct.discriminator.path,
371
+ required: ct.discriminator.required === true,
372
+ ...(ct.discriminator.defaultValue ? { defaultValue: ct.discriminator.defaultValue } : {}),
373
+ },
374
+ }
375
+ : {}),
376
+ };
377
+ }
378
+
379
+ const ct = pack.objectTypes.find((o) => o.id === record.objectType)!
380
+ .contentTypes.find((c) => c.id === record.contentType)!;
381
+ const result = validateMemorixRecordV2(record, { profile: toProfile(ct), enforceDiscriminator: true });
382
+ ```
383
+
296
384
  ---
297
385
 
298
386
  ## 6. Abstract types and platform interfaces
@@ -414,7 +502,7 @@ Run with `executeMapping`, `simulateMapping` (adds coverage), and `executeRevers
414
502
 
415
503
  ## 9. Worked example
416
504
 
417
- A pack fragment with one OT, two CTs, an abstract with a direct binding, a source mapping, and an abstract mapping. A complete real pack is in `memorix-metadata/examples/opx/`.
505
+ A pack fragment with one OT, two CTs, an abstract with a direct binding, a source mapping, and an abstract mapping. A complete real pack is `examples/opx/normalized/pack.json` in `@x12i/memorix-metadata`.
418
506
 
419
507
  ```json
420
508
  {
@@ -566,12 +654,20 @@ A stored record for that OT/CT, in collection `work-items-snapshots` of database
566
654
 
567
655
  ## Related documents
568
656
 
569
- These ship inside `@x12i/memorix-format`:
657
+ In `@x12i/memorix-format`:
570
658
 
571
659
  - `docs/memorix-record-v2.md`: the full envelope contract and its locked decisions.
572
- - `docs/migrate-present-to-v2.md`: migrating the older present envelope.
660
+ - `docs/migrate-present-to-v2.md`: migrating the older present envelope to `memorix-record/2`.
573
661
  - `docs/memorix-workflow-format.md`: the `workflow` content type.
574
- - `docs/association-format.md`, `docs/memorix-format.md`, `docs/memorix-format-requirements.md`.
575
- - `meta/`: envelope, open-bucket, and content-type vocabulary.
662
+ - `docs/memorix-format.md`, `docs/memorix-format-requirements.md`, `docs/association-format.md`: the older present envelope. Read them only to migrate existing data.
663
+ - `meta/`: envelope, open-bucket, and content-type vocabulary as JSON.
664
+ - `examples/v2/`: valid `memorix-record/2` documents.
665
+
666
+ In `@x12i/memorix-metadata`:
667
+
668
+ - `examples/opx/`, `examples/knowx/`, `examples/analytics/`, `examples/custom-kinds/`: valid packs.
669
+ - `examples/invalid/`: packs that fail validation, one rule each.
670
+
671
+ In `@x12i/memorix-mapping`:
576
672
 
577
- `@x12i/memorix-metadata` ships this reference, its `README.md`, and `examples/`. `@x12i/memorix-mapping` ships its API in `README.md`.
673
+ - `examples/`: source, abstract, and completion mappings.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/memorix-metadata",
3
- "version": "3.6.2",
3
+ "version": "3.6.3",
4
4
  "description": "Memorix object-type and content-type descriptors, metadata pack contracts, and validation. Depends only on @x12i/memorix-mapping.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,7 +23,8 @@
23
23
  "build": "tsc -p tsconfig.build.json",
24
24
  "test": "vitest run",
25
25
  "test:watch": "vitest",
26
- "typecheck": "tsc --noEmit"
26
+ "typecheck": "tsc --noEmit",
27
+ "prepack": "node ../scripts/sync-format-reference.mjs && npm run build"
27
28
  },
28
29
  "engines": {
29
30
  "node": ">=18.0.0"