@x12i/memorix-format 3.0.3 → 3.0.6
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 +73 -65
- package/README.md +78 -78
- package/dist/base/concept.d.ts +4 -0
- package/dist/base/concept.d.ts.map +1 -1
- package/dist/base/concept.js +5 -1
- package/dist/base/concept.js.map +1 -1
- package/dist/cli.js +36 -36
- package/dist/v2/identity.d.ts +19 -0
- package/dist/v2/identity.d.ts.map +1 -0
- package/dist/v2/identity.js +39 -0
- package/dist/v2/identity.js.map +1 -0
- package/dist/v2/index.d.ts +2 -1
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/index.js +1 -0
- package/dist/v2/index.js.map +1 -1
- package/dist/v2/migrate-present.js +1 -1
- package/dist/v2/migrate-present.js.map +1 -1
- package/dist/v2/profiles.d.ts +1 -1
- package/dist/v2/profiles.d.ts.map +1 -1
- package/dist/v2/profiles.js +1 -0
- package/dist/v2/profiles.js.map +1 -1
- package/dist/v2/types.d.ts +24 -0
- package/dist/v2/types.d.ts.map +1 -1
- package/dist/v2/validate.d.ts.map +1 -1
- package/dist/v2/validate.js +18 -1
- package/dist/v2/validate.js.map +1 -1
- package/docs/association-format.md +47 -47
- package/docs/memorix-format-requirements.md +54 -54
- package/docs/memorix-format.md +68 -68
- package/docs/memorix-record-v2.md +102 -101
- package/docs/memorix-workflow-format.md +126 -126
- package/docs/migrate-present-to-v2.md +49 -49
- package/examples/invalid-record.json +11 -11
- package/examples/v2/activity-events-snapshots.json +31 -31
- package/examples/v2/people-discovery.json +26 -26
- package/examples/v2/people-snapshots-employee.json +27 -27
- package/examples/v2/playbooks-knowledge.json +27 -27
- package/examples/v2/work-items-analysis.json +26 -26
- package/examples/v2/work-items-context.json +32 -32
- package/examples/v2/work-items-decisions.json +26 -26
- package/examples/v2/work-items-raw.json +37 -37
- package/examples/v2/work-items-relationships-candidate.json +38 -38
- package/examples/v2/work-items-snapshots-lifecycle.json +31 -31
- package/examples/v2/work-items-snapshots-lightweight-refs.json +34 -34
- package/examples/v2/work-items-snapshots.json +28 -28
- package/examples/v2/work-items-workflow.json +28 -28
- package/examples/valid-snapshot.json +30 -30
- package/meta/associated-conventions.json +59 -59
- package/meta/content-type-profiles.json +27 -27
- package/meta/content-types.json +80 -80
- package/meta/envelope.json +104 -104
- package/meta/inventory/managed-descriptors.json +78 -78
- package/meta/open-buckets.json +24 -24
- package/package.json +77 -77
|
@@ -1,101 +1,102 @@
|
|
|
1
|
-
# memorix-record/2
|
|
2
|
-
|
|
3
|
-
**Status:** Gate G2 contract — preferred source of truth for new platform work.
|
|
4
|
-
**Package:** `@x12i/memorix-format` (`validateMemorixRecordV2`, `migratePresentToV2`, CLI `validate-v2` / `migrate-to-v2`).
|
|
5
|
-
|
|
6
|
-
Present-tense envelope docs remain in [memorix-format.md](./memorix-format.md) for migration only.
|
|
7
|
-
Migration semantics: [migrate-present-to-v2.md](./migrate-present-to-v2.md).
|
|
8
|
-
|
|
9
|
-
## Envelope
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
type MemorixRecordV2 = {
|
|
13
|
-
format: "memorix-record/2";
|
|
14
|
-
recordId: string;
|
|
15
|
-
revision: number; // starts at 1; increments every successful mutation
|
|
16
|
-
objectType: string;
|
|
17
|
-
contentType: string;
|
|
18
|
-
dataCategory: "entity" | "event" | "knowledge";
|
|
19
|
-
concept: {
|
|
20
|
-
title: string;
|
|
21
|
-
identifiers: {
|
|
22
|
-
primary: { kind: string; value: string; issuer?: string; normalizedValue?: string };
|
|
23
|
-
alternates?: Array<...>; // omit when empty
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
data: Record<string, unknown>;
|
|
27
|
-
analytics?: Record<string, unknown>; // snapshots (and knowledge) only; omit when unused
|
|
28
|
-
enrichment?: Record<string, unknown>;
|
|
29
|
-
insights?: Record<string, unknown>;
|
|
30
|
-
// Option A — lightweight relationship refs remain FORMAT-VALID (G2).
|
|
31
|
-
// Gate G4 selected authoritative storage = central per-category collections (Candidate C).
|
|
32
|
-
// associatedPeople?: Array<{
|
|
33
|
-
// relationshipDefinitionId: string; // required
|
|
34
|
-
// recordId: string; // required
|
|
35
|
-
// relationshipId?: string; // optional — deterministically derivable
|
|
36
|
-
// }>;
|
|
37
|
-
_system: {
|
|
38
|
-
state: "active" | "archived" | "superseded";
|
|
39
|
-
createdAt: string;
|
|
40
|
-
modifiedAt: string;
|
|
41
|
-
observedAt?: string;
|
|
42
|
-
lifecycle?: Array<{ process: string; processedAt: string }>; // omit when empty
|
|
43
|
-
provenance?: Record<string, unknown>;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Locked decisions (Gate G2)
|
|
49
|
-
|
|
50
|
-
1. **Empty optional snapshot buckets are omitted** — never emit `{}`.
|
|
51
|
-
2. **Workflow is a sibling content record** (`contentType: "workflow"`), not a separate document system.
|
|
52
|
-
3. **Composition** = same `recordId` across content types; not a relationship.
|
|
53
|
-
4. **`_graphRuns` / `_jobTypeRuns` stay off the subject** — operational run ledger owns run history.
|
|
54
|
-
5. **V2 rejects copied related-record payloads.** Root `associated*` may hold **lightweight relationship refs** only: required `relationshipDefinitionId` + `recordId`; optional `relationshipId` (derivable). Without `relationshipDefinitionId` the item is not a relationship. **Option A** (direct-record lightweight `associated*` roots) remains **format-valid** for validation and migration compatibility.
|
|
55
|
-
6. **Authoritative physical relationship storage is Gate G4 Candidate C** — org-prefixed per-category `relationships` collections (`entity` / `event` / `knowledge`). Option A lightweight roots and sibling `relationships` content are **not** the selected authoritative storage model; see `@x12i/memorix-relationship-store` / `@x12i/memorix-relationships` (decision note archived under `.tmp/housekeeping-2026-07-23/docs/re-factoring-and-opx-agent/RELATIONSHIP-STORAGE-DECISION.md`).
|
|
56
|
-
7. **
|
|
57
|
-
|
|
58
|
-
## Revision vs lifecycle
|
|
59
|
-
|
|
60
|
-
| Rule | Behavior |
|
|
61
|
-
|---|---|
|
|
62
|
-
| Revision | Increments on every successful mutation |
|
|
63
|
-
| Lifecycle | Appended only for meaningful process/state events |
|
|
64
|
-
| Independence | Revision may change with no lifecycle entry |
|
|
65
|
-
| Concurrency | Stale `expectedRevision` is rejected (`checkExpectedRevision`) |
|
|
66
|
-
|
|
67
|
-
Helpers: `applySuccessfulMutation`, `nextRevision`, `checkExpectedRevision`.
|
|
68
|
-
|
|
69
|
-
## Discriminators
|
|
70
|
-
|
|
71
|
-
- Pure structural V2 validation does not invent discriminator values.
|
|
72
|
-
- Content-type **profiles** (from metadata) may declare `path`, `required`, `defaultValue`, `allowedValues`.
|
|
73
|
-
- Missing + profile default → warning `V2-DISCRIMINATOR-DEFAULT` (writers apply default).
|
|
74
|
-
- Missing + required + no default → error `V2-DISCRIMINATOR`.
|
|
75
|
-
|
|
76
|
-
## Examples
|
|
77
|
-
|
|
78
|
-
See [`examples/v2/`](../examples/v2/) — includes lifecycle, lightweight refs, analysis/decision/discovery, workflow sibling, relationships-candidate sibling.
|
|
79
|
-
|
|
80
|
-
## Migrate
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
npx memorix-format migrate-to-v2 ./present.json \
|
|
84
|
-
--object-type=work-items \
|
|
85
|
-
--content-type=snapshots \
|
|
86
|
-
--data-category=entity \
|
|
87
|
-
--issuer=opx-source
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Output includes `v2`, `extractedRelationships`, and `warnings`.
|
|
91
|
-
|
|
92
|
-
## Writer cutover flag
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
1
|
+
# memorix-record/2
|
|
2
|
+
|
|
3
|
+
**Status:** Gate G2 contract — preferred source of truth for new platform work.
|
|
4
|
+
**Package:** `@x12i/memorix-format` (`validateMemorixRecordV2`, `migratePresentToV2`, CLI `validate-v2` / `migrate-to-v2`).
|
|
5
|
+
|
|
6
|
+
Present-tense envelope docs remain in [memorix-format.md](./memorix-format.md) for migration only.
|
|
7
|
+
Migration semantics: [migrate-present-to-v2.md](./migrate-present-to-v2.md).
|
|
8
|
+
|
|
9
|
+
## Envelope
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
type MemorixRecordV2 = {
|
|
13
|
+
format: "memorix-record/2";
|
|
14
|
+
recordId: string;
|
|
15
|
+
revision: number; // starts at 1; increments every successful mutation
|
|
16
|
+
objectType: string;
|
|
17
|
+
contentType: string;
|
|
18
|
+
dataCategory: "entity" | "event" | "knowledge";
|
|
19
|
+
concept: {
|
|
20
|
+
title: string;
|
|
21
|
+
identifiers: {
|
|
22
|
+
primary: { kind: string; value: string; issuer?: string; normalizedValue?: string };
|
|
23
|
+
alternates?: Array<...>; // omit when empty
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
data: Record<string, unknown>;
|
|
27
|
+
analytics?: Record<string, unknown>; // snapshots (and knowledge) only; omit when unused
|
|
28
|
+
enrichment?: Record<string, unknown>;
|
|
29
|
+
insights?: Record<string, unknown>;
|
|
30
|
+
// Option A — lightweight relationship refs remain FORMAT-VALID (G2).
|
|
31
|
+
// Gate G4 selected authoritative storage = central per-category collections (Candidate C).
|
|
32
|
+
// associatedPeople?: Array<{
|
|
33
|
+
// relationshipDefinitionId: string; // required
|
|
34
|
+
// recordId: string; // required
|
|
35
|
+
// relationshipId?: string; // optional — deterministically derivable
|
|
36
|
+
// }>;
|
|
37
|
+
_system: {
|
|
38
|
+
state: "active" | "archived" | "superseded";
|
|
39
|
+
createdAt: string;
|
|
40
|
+
modifiedAt: string;
|
|
41
|
+
observedAt?: string;
|
|
42
|
+
lifecycle?: Array<{ process: string; processedAt: string }>; // omit when empty
|
|
43
|
+
provenance?: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Locked decisions (Gate G2)
|
|
49
|
+
|
|
50
|
+
1. **Empty optional snapshot buckets are omitted** — never emit `{}`.
|
|
51
|
+
2. **Workflow is a sibling content record** (`contentType: "workflow"`), not a separate document system.
|
|
52
|
+
3. **Composition** = same `recordId` across content types; not a relationship.
|
|
53
|
+
4. **`_graphRuns` / `_jobTypeRuns` stay off the subject** — operational run ledger owns run history.
|
|
54
|
+
5. **V2 rejects copied related-record payloads.** Root `associated*` may hold **lightweight relationship refs** only: required `relationshipDefinitionId` + `recordId`; optional `relationshipId` (derivable). Without `relationshipDefinitionId` the item is not a relationship. **Option A** (direct-record lightweight `associated*` roots) remains **format-valid** for validation and migration compatibility.
|
|
55
|
+
6. **Authoritative physical relationship storage is Gate G4 Candidate C** — org-prefixed per-category `relationships` collections (`entity` / `event` / `knowledge`). Option A lightweight roots and sibling `relationships` content are **not** the selected authoritative storage model; see `@x12i/memorix-relationship-store` / `@x12i/memorix-relationships` (decision note archived under `.tmp/housekeeping-2026-07-23/docs/re-factoring-and-opx-agent/RELATIONSHIP-STORAGE-DECISION.md`).
|
|
56
|
+
7. **Writers persist `memorix-record/2` exclusively.** The historical `MEMORIX_RECORD_FORMAT=v2` switch is obsolete and must not be documented as optional. Present-format docs remain for migration tooling only.
|
|
57
|
+
|
|
58
|
+
## Revision vs lifecycle
|
|
59
|
+
|
|
60
|
+
| Rule | Behavior |
|
|
61
|
+
|---|---|
|
|
62
|
+
| Revision | Increments on every successful mutation |
|
|
63
|
+
| Lifecycle | Appended only for meaningful process/state events |
|
|
64
|
+
| Independence | Revision may change with no lifecycle entry |
|
|
65
|
+
| Concurrency | Stale `expectedRevision` is rejected (`checkExpectedRevision`) |
|
|
66
|
+
|
|
67
|
+
Helpers: `applySuccessfulMutation`, `nextRevision`, `checkExpectedRevision`.
|
|
68
|
+
|
|
69
|
+
## Discriminators
|
|
70
|
+
|
|
71
|
+
- Pure structural V2 validation does not invent discriminator values.
|
|
72
|
+
- Content-type **profiles** (from metadata) may declare `path`, `required`, `defaultValue`, `allowedValues`.
|
|
73
|
+
- Missing + profile default → warning `V2-DISCRIMINATOR-DEFAULT` (writers apply default).
|
|
74
|
+
- Missing + required + no default → error `V2-DISCRIMINATOR`.
|
|
75
|
+
|
|
76
|
+
## Examples
|
|
77
|
+
|
|
78
|
+
See [`examples/v2/`](../examples/v2/) — includes lifecycle, lightweight refs, analysis/decision/discovery, workflow sibling, relationships-candidate sibling.
|
|
79
|
+
|
|
80
|
+
## Migrate
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx memorix-format migrate-to-v2 ./present.json \
|
|
84
|
+
--object-type=work-items \
|
|
85
|
+
--content-type=snapshots \
|
|
86
|
+
--data-category=entity \
|
|
87
|
+
--issuer=opx-source
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Output includes `v2`, `extractedRelationships`, and `warnings`.
|
|
91
|
+
|
|
92
|
+
## Writer cutover flag
|
|
93
|
+
|
|
94
|
+
Obsolete. Production writers always validate and persist `memorix-record/2`. Do not set `MEMORIX_RECORD_FORMAT`.
|
|
95
|
+
|
|
96
|
+
## Subject identity
|
|
97
|
+
|
|
98
|
+
See [the subject-model contract](../../docs/subject-model-contract.md). `recordId` is never Mongo `_id`. Source identifiers are adopted as `{issuer}/{kind}/{value}`.
|
|
99
|
+
|
|
100
|
+
## Folding `@x12i/memorix-base-format`
|
|
101
|
+
|
|
102
|
+
Present base validation still depends on `@x12i/memorix-base-format`. That package becomes a temporary re-export after consumers move to `@x12i/memorix-format` / V2, then is deleted (Phase 0 deletion ledger).
|
|
@@ -1,126 +1,126 @@
|
|
|
1
|
-
# Memorix Workflow Format
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
**Gate G2:** Workflow is a **sibling content record** on `memorix-record/2` with
|
|
6
|
-
`contentType: "workflow"` and the same `recordId` as the subject — not a separate
|
|
7
|
-
document system. See [memorix-record-v2.md](./memorix-record-v2.md) and
|
|
8
|
-
`examples/v2/work-items-workflow.json`.
|
|
9
|
-
|
|
10
|
-
Legacy product shape (`memorix-workflow/1`) below remains for migration until
|
|
11
|
-
workflow-api cutover.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Legacy: `memorix-workflow/1`
|
|
16
|
-
|
|
17
|
-
Versioned JSON for **product workflow state** attached to a subject object via the
|
|
18
|
-
content type `workflow` (collection `{objectType}-workflow`).
|
|
19
|
-
|
|
20
|
-
This is **not** inventory (snapshots), **not** AI analysis/decision, and **not**
|
|
21
|
-
vendor audit telemetry. It stores human/product work state: assignee, status,
|
|
22
|
-
append-only audit of UI mutations, and close-via tasks.
|
|
23
|
-
|
|
24
|
-
Catalog `memorix-workflow-field-schemas` defines which scalar/enum fields may
|
|
25
|
-
appear under `data` (beyond reserved `audit` / `tasks`).
|
|
26
|
-
|
|
27
|
-
## Collection postfix
|
|
28
|
-
|
|
29
|
-
| Descriptor content-type key | Postfix | Collection | Cardinality |
|
|
30
|
-
|---|---|---|---|
|
|
31
|
-
| `workflow` | `workflow` | `{objectType}-workflow` | `1:1` |
|
|
32
|
-
|
|
33
|
-
Join key: same `recordId` as the subject entity’s canonical snapshot.
|
|
34
|
-
|
|
35
|
-
## Canonical document
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
type MemorixWorkflowDocument = {
|
|
39
|
-
format: "memorix-workflow/1";
|
|
40
|
-
recordId: string;
|
|
41
|
-
objectType: string;
|
|
42
|
-
data: {
|
|
43
|
-
/** Catalog-driven properties (validated against field schema). */
|
|
44
|
-
[fieldKey: string]: unknown;
|
|
45
|
-
/** Format-reserved — API-managed only. */
|
|
46
|
-
audit: WorkflowAuditEntry[];
|
|
47
|
-
tasks: WorkflowTask[];
|
|
48
|
-
};
|
|
49
|
-
modifiedAt: string;
|
|
50
|
-
modifiedBy?: string;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
type WorkflowAuditEntry = {
|
|
54
|
-
id: string;
|
|
55
|
-
at: string;
|
|
56
|
-
actor: string;
|
|
57
|
-
action: string;
|
|
58
|
-
field?: string;
|
|
59
|
-
previous?: unknown;
|
|
60
|
-
next?: unknown;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
type WorkflowTask = {
|
|
64
|
-
id: string;
|
|
65
|
-
dk: string;
|
|
66
|
-
title: string;
|
|
67
|
-
via?: string;
|
|
68
|
-
status: "open" | "done" | "cancelled";
|
|
69
|
-
createdAt: string;
|
|
70
|
-
createdBy: string;
|
|
71
|
-
completedAt?: string;
|
|
72
|
-
};
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Rules
|
|
76
|
-
|
|
77
|
-
1. Custom fields under `data` **must** be declared in `memorix-workflow-field-schemas`
|
|
78
|
-
for that `objectType` (when `rejectUnknownFields` is true, default).
|
|
79
|
-
2. `audit` and `tasks` are **never** writable via generic field PATCH — dedicated
|
|
80
|
-
API endpoints only.
|
|
81
|
-
3. Enum values are stored as English **keys**; UI maps display labels.
|
|
82
|
-
4. Do not put workflow state on snapshots, analysis, or decision documents.
|
|
83
|
-
|
|
84
|
-
## Field schema catalog
|
|
85
|
-
|
|
86
|
-
Catalog id: `memorix-workflow-field-schemas`
|
|
87
|
-
|
|
88
|
-
One item per object type (natural id e.g. `items-workflow-fields`):
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
type WorkflowFieldSchemaItem = {
|
|
92
|
-
id: string;
|
|
93
|
-
objectType: string;
|
|
94
|
-
contentType: "workflow";
|
|
95
|
-
rejectUnknownFields: boolean;
|
|
96
|
-
fields: WorkflowFieldDef[];
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
type WorkflowFieldDef = {
|
|
100
|
-
key: string;
|
|
101
|
-
label: string;
|
|
102
|
-
description?: string;
|
|
103
|
-
valueType: "string" | "number" | "boolean" | "datetime" | "enum";
|
|
104
|
-
enumValues?: string[];
|
|
105
|
-
required?: boolean;
|
|
106
|
-
writable?: boolean;
|
|
107
|
-
defaultValue?: unknown;
|
|
108
|
-
list?: boolean;
|
|
109
|
-
item?: boolean;
|
|
110
|
-
section?: string;
|
|
111
|
-
order?: number;
|
|
112
|
-
};
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## API
|
|
116
|
-
|
|
117
|
-
See `@x12i/memorix-workflow-api` — catalog-driven HTTP for schema list/get/upsert,
|
|
118
|
-
overview aggregation, document list/get/ensure, field PATCH, audit list, and
|
|
119
|
-
tasks CRUD. UI: `@x12i/memorix-workflow-ui`.
|
|
120
|
-
|
|
121
|
-
List/overview notes:
|
|
122
|
-
|
|
123
|
-
- `GET /api/workflow/schemas` — discover object types with field schemas
|
|
124
|
-
- `GET /api/workflow/:objectType` — list existing workflow overlays (slim `list:true` projection)
|
|
125
|
-
- `GET /api/workflow/overview` — totals and recent audit activity
|
|
126
|
-
- `GET .../audit` and `GET .../tasks` are read-only (404 when the document is missing; they do not create)
|
|
1
|
+
# Memorix Workflow Format
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
**Gate G2:** Workflow is a **sibling content record** on `memorix-record/2` with
|
|
6
|
+
`contentType: "workflow"` and the same `recordId` as the subject — not a separate
|
|
7
|
+
document system. See [memorix-record-v2.md](./memorix-record-v2.md) and
|
|
8
|
+
`examples/v2/work-items-workflow.json`.
|
|
9
|
+
|
|
10
|
+
Legacy product shape (`memorix-workflow/1`) below remains for migration until
|
|
11
|
+
workflow-api cutover.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Legacy: `memorix-workflow/1`
|
|
16
|
+
|
|
17
|
+
Versioned JSON for **product workflow state** attached to a subject object via the
|
|
18
|
+
content type `workflow` (collection `{objectType}-workflow`).
|
|
19
|
+
|
|
20
|
+
This is **not** inventory (snapshots), **not** AI analysis/decision, and **not**
|
|
21
|
+
vendor audit telemetry. It stores human/product work state: assignee, status,
|
|
22
|
+
append-only audit of UI mutations, and close-via tasks.
|
|
23
|
+
|
|
24
|
+
Catalog `memorix-workflow-field-schemas` defines which scalar/enum fields may
|
|
25
|
+
appear under `data` (beyond reserved `audit` / `tasks`).
|
|
26
|
+
|
|
27
|
+
## Collection postfix
|
|
28
|
+
|
|
29
|
+
| Descriptor content-type key | Postfix | Collection | Cardinality |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `workflow` | `workflow` | `{objectType}-workflow` | `1:1` |
|
|
32
|
+
|
|
33
|
+
Join key: same `recordId` as the subject entity’s canonical snapshot.
|
|
34
|
+
|
|
35
|
+
## Canonical document
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
type MemorixWorkflowDocument = {
|
|
39
|
+
format: "memorix-workflow/1";
|
|
40
|
+
recordId: string;
|
|
41
|
+
objectType: string;
|
|
42
|
+
data: {
|
|
43
|
+
/** Catalog-driven properties (validated against field schema). */
|
|
44
|
+
[fieldKey: string]: unknown;
|
|
45
|
+
/** Format-reserved — API-managed only. */
|
|
46
|
+
audit: WorkflowAuditEntry[];
|
|
47
|
+
tasks: WorkflowTask[];
|
|
48
|
+
};
|
|
49
|
+
modifiedAt: string;
|
|
50
|
+
modifiedBy?: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type WorkflowAuditEntry = {
|
|
54
|
+
id: string;
|
|
55
|
+
at: string;
|
|
56
|
+
actor: string;
|
|
57
|
+
action: string;
|
|
58
|
+
field?: string;
|
|
59
|
+
previous?: unknown;
|
|
60
|
+
next?: unknown;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type WorkflowTask = {
|
|
64
|
+
id: string;
|
|
65
|
+
dk: string;
|
|
66
|
+
title: string;
|
|
67
|
+
via?: string;
|
|
68
|
+
status: "open" | "done" | "cancelled";
|
|
69
|
+
createdAt: string;
|
|
70
|
+
createdBy: string;
|
|
71
|
+
completedAt?: string;
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Rules
|
|
76
|
+
|
|
77
|
+
1. Custom fields under `data` **must** be declared in `memorix-workflow-field-schemas`
|
|
78
|
+
for that `objectType` (when `rejectUnknownFields` is true, default).
|
|
79
|
+
2. `audit` and `tasks` are **never** writable via generic field PATCH — dedicated
|
|
80
|
+
API endpoints only.
|
|
81
|
+
3. Enum values are stored as English **keys**; UI maps display labels.
|
|
82
|
+
4. Do not put workflow state on snapshots, analysis, or decision documents.
|
|
83
|
+
|
|
84
|
+
## Field schema catalog
|
|
85
|
+
|
|
86
|
+
Catalog id: `memorix-workflow-field-schemas`
|
|
87
|
+
|
|
88
|
+
One item per object type (natural id e.g. `items-workflow-fields`):
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
type WorkflowFieldSchemaItem = {
|
|
92
|
+
id: string;
|
|
93
|
+
objectType: string;
|
|
94
|
+
contentType: "workflow";
|
|
95
|
+
rejectUnknownFields: boolean;
|
|
96
|
+
fields: WorkflowFieldDef[];
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
type WorkflowFieldDef = {
|
|
100
|
+
key: string;
|
|
101
|
+
label: string;
|
|
102
|
+
description?: string;
|
|
103
|
+
valueType: "string" | "number" | "boolean" | "datetime" | "enum";
|
|
104
|
+
enumValues?: string[];
|
|
105
|
+
required?: boolean;
|
|
106
|
+
writable?: boolean;
|
|
107
|
+
defaultValue?: unknown;
|
|
108
|
+
list?: boolean;
|
|
109
|
+
item?: boolean;
|
|
110
|
+
section?: string;
|
|
111
|
+
order?: number;
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## API
|
|
116
|
+
|
|
117
|
+
See `@x12i/memorix-workflow-api` — catalog-driven HTTP for schema list/get/upsert,
|
|
118
|
+
overview aggregation, document list/get/ensure, field PATCH, audit list, and
|
|
119
|
+
tasks CRUD. UI: `@x12i/memorix-workflow-ui`.
|
|
120
|
+
|
|
121
|
+
List/overview notes:
|
|
122
|
+
|
|
123
|
+
- `GET /api/workflow/schemas` — discover object types with field schemas
|
|
124
|
+
- `GET /api/workflow/:objectType` — list existing workflow overlays (slim `list:true` projection)
|
|
125
|
+
- `GET /api/workflow/overview` — totals and recent audit activity
|
|
126
|
+
- `GET .../audit` and `GET .../tasks` are read-only (404 when the document is missing; they do not create)
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
# memorix-record/2 — present → V2 migration semantics
|
|
2
|
-
|
|
3
|
-
## Result shape
|
|
4
|
-
|
|
5
|
-
`migratePresentToV2` returns:
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
{
|
|
9
|
-
ok: boolean;
|
|
10
|
-
v2: MemorixRecordV2; // clean V2; no copied associated payloads
|
|
11
|
-
extractedRelationships: [...]; // out-of-band; NOT persisted (Gate G4)
|
|
12
|
-
warnings: string[];
|
|
13
|
-
}
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Associated* handling
|
|
17
|
-
|
|
18
|
-
| Input | V2 record | extractedRelationships |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| Item with `recordId` (+ copied fields) | `associated*` removed from V2 | status `mapped` or `pending-metadata-resolution`; `droppedPayloadKeys` lists copied fields |
|
|
21
|
-
| Item without target `recordId` | removed | `quarantined-missing-recordId` |
|
|
22
|
-
| Non-array associated* | removed | warning; skipped |
|
|
23
|
-
|
|
24
|
-
Copied payload is never written onto V2. Resolvable links are never silently discarded.
|
|
25
|
-
|
|
26
|
-
Optional `associatedPropertyMap: { associatedPeople: "people-assigned-to-work-items" }` supplies `relationshipDefinitionId` when known.
|
|
27
|
-
|
|
28
|
-
## Timestamps / identifiers
|
|
29
|
-
|
|
30
|
-
| Situation | Behavior |
|
|
31
|
-
|---|---|
|
|
32
|
-
| `createdAt` missing | default `now`; warning |
|
|
33
|
-
| `modifiedAt` missing | default to `createdAt`/`now`; warning |
|
|
34
|
-
| kind-named value missing | fall back to `value` / key tail / `recordId`; warning |
|
|
35
|
-
| Multiple old identifiers | primary from `_extIdentifier` only; extras not auto-alternates (warning path for future) |
|
|
36
|
-
| Unrecognized roots | dropped with warning |
|
|
37
|
-
| Subject `_graphRuns` etc. | dropped with warning (ops ledger owns run history) |
|
|
38
|
-
|
|
39
|
-
## Success vs failure
|
|
40
|
-
|
|
41
|
-
- Migration **succeeds with warnings** when a V2 envelope is produced (`ok: true`) even if relationships are pending/quarantined.
|
|
42
|
-
- Callers should still run `validateMemorixRecordV2(result.v2)`.
|
|
43
|
-
- Migration does not throw on missing association targets; it quarantines.
|
|
44
|
-
|
|
45
|
-
## Gate G4 boundary
|
|
46
|
-
|
|
47
|
-
**Authoritative physical relationship storage (Gate G4):** centralized org-prefixed per-category `relationships` collections (Candidate C). See `@x12i/memorix-relationship-store` (archived decision: `.tmp/housekeeping-2026-07-23/docs/re-factoring-and-opx-agent/RELATIONSHIP-STORAGE-DECISION.md`).
|
|
48
|
-
|
|
49
|
-
`migratePresentToV2` still extracts relationship candidates **out-of-band** and does not persist them onto the V2 record. Option A root `associated*` lightweight refs remain **format-valid** but are **not** the selected authoritative storage model.
|
|
1
|
+
# memorix-record/2 — present → V2 migration semantics
|
|
2
|
+
|
|
3
|
+
## Result shape
|
|
4
|
+
|
|
5
|
+
`migratePresentToV2` returns:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
{
|
|
9
|
+
ok: boolean;
|
|
10
|
+
v2: MemorixRecordV2; // clean V2; no copied associated payloads
|
|
11
|
+
extractedRelationships: [...]; // out-of-band; NOT persisted (Gate G4)
|
|
12
|
+
warnings: string[];
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Associated* handling
|
|
17
|
+
|
|
18
|
+
| Input | V2 record | extractedRelationships |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| Item with `recordId` (+ copied fields) | `associated*` removed from V2 | status `mapped` or `pending-metadata-resolution`; `droppedPayloadKeys` lists copied fields |
|
|
21
|
+
| Item without target `recordId` | removed | `quarantined-missing-recordId` |
|
|
22
|
+
| Non-array associated* | removed | warning; skipped |
|
|
23
|
+
|
|
24
|
+
Copied payload is never written onto V2. Resolvable links are never silently discarded.
|
|
25
|
+
|
|
26
|
+
Optional `associatedPropertyMap: { associatedPeople: "people-assigned-to-work-items" }` supplies `relationshipDefinitionId` when known.
|
|
27
|
+
|
|
28
|
+
## Timestamps / identifiers
|
|
29
|
+
|
|
30
|
+
| Situation | Behavior |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `createdAt` missing | default `now`; warning |
|
|
33
|
+
| `modifiedAt` missing | default to `createdAt`/`now`; warning |
|
|
34
|
+
| kind-named value missing | fall back to `value` / key tail / `recordId`; warning |
|
|
35
|
+
| Multiple old identifiers | primary from `_extIdentifier` only; extras not auto-alternates (warning path for future) |
|
|
36
|
+
| Unrecognized roots | dropped with warning |
|
|
37
|
+
| Subject `_graphRuns` etc. | dropped with warning (ops ledger owns run history) |
|
|
38
|
+
|
|
39
|
+
## Success vs failure
|
|
40
|
+
|
|
41
|
+
- Migration **succeeds with warnings** when a V2 envelope is produced (`ok: true`) even if relationships are pending/quarantined.
|
|
42
|
+
- Callers should still run `validateMemorixRecordV2(result.v2)`.
|
|
43
|
+
- Migration does not throw on missing association targets; it quarantines.
|
|
44
|
+
|
|
45
|
+
## Gate G4 boundary
|
|
46
|
+
|
|
47
|
+
**Authoritative physical relationship storage (Gate G4):** centralized org-prefixed per-category `relationships` collections (Candidate C). See `@x12i/memorix-relationship-store` (archived decision: `.tmp/housekeeping-2026-07-23/docs/re-factoring-and-opx-agent/RELATIONSHIP-STORAGE-DECISION.md`).
|
|
48
|
+
|
|
49
|
+
`migratePresentToV2` still extracts relationship candidates **out-of-band** and does not persist them onto the V2 record. Option A root `associated*` lightweight refs remain **format-valid** but are **not** the selected authoritative storage model.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
{
|
|
2
|
-
"recordId": "",
|
|
3
|
-
"concept": {
|
|
4
|
-
"name": "legacy-name-only"
|
|
5
|
-
},
|
|
6
|
-
"data": "not-an-object",
|
|
7
|
-
"identity": {
|
|
8
|
-
"version": "memorix-record.2.0"
|
|
9
|
-
},
|
|
10
|
-
"analytics": []
|
|
11
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"recordId": "",
|
|
3
|
+
"concept": {
|
|
4
|
+
"name": "legacy-name-only"
|
|
5
|
+
},
|
|
6
|
+
"data": "not-an-object",
|
|
7
|
+
"identity": {
|
|
8
|
+
"version": "memorix-record.2.0"
|
|
9
|
+
},
|
|
10
|
+
"analytics": []
|
|
11
|
+
}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
{
|
|
2
|
-
"format": "memorix-record/2",
|
|
3
|
-
"recordId": "ae-500",
|
|
4
|
-
"revision": 1,
|
|
5
|
-
"objectType": "activity-events",
|
|
6
|
-
"contentType": "snapshots",
|
|
7
|
-
"dataCategory": "event",
|
|
8
|
-
"concept": {
|
|
9
|
-
"title": "status-note on WI-100",
|
|
10
|
-
"identifiers": {
|
|
11
|
-
"primary": {
|
|
12
|
-
"kind": "activityEventId",
|
|
13
|
-
"value": "AE-500",
|
|
14
|
-
"issuer": "opx-source"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"data": {
|
|
19
|
-
"workItemId": "WI-100",
|
|
20
|
-
"actorId": "E-10",
|
|
21
|
-
"type": "status-note",
|
|
22
|
-
"message": "Draft outline complete",
|
|
23
|
-
"occurredAt": "2026-01-15T14:00:00.000Z"
|
|
24
|
-
},
|
|
25
|
-
"_system": {
|
|
26
|
-
"state": "active",
|
|
27
|
-
"createdAt": "2026-01-15T14:00:00.000Z",
|
|
28
|
-
"modifiedAt": "2026-01-15T14:00:00.000Z",
|
|
29
|
-
"observedAt": "2026-01-15T14:00:00.000Z"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"format": "memorix-record/2",
|
|
3
|
+
"recordId": "ae-500",
|
|
4
|
+
"revision": 1,
|
|
5
|
+
"objectType": "activity-events",
|
|
6
|
+
"contentType": "snapshots",
|
|
7
|
+
"dataCategory": "event",
|
|
8
|
+
"concept": {
|
|
9
|
+
"title": "status-note on WI-100",
|
|
10
|
+
"identifiers": {
|
|
11
|
+
"primary": {
|
|
12
|
+
"kind": "activityEventId",
|
|
13
|
+
"value": "AE-500",
|
|
14
|
+
"issuer": "opx-source"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"data": {
|
|
19
|
+
"workItemId": "WI-100",
|
|
20
|
+
"actorId": "E-10",
|
|
21
|
+
"type": "status-note",
|
|
22
|
+
"message": "Draft outline complete",
|
|
23
|
+
"occurredAt": "2026-01-15T14:00:00.000Z"
|
|
24
|
+
},
|
|
25
|
+
"_system": {
|
|
26
|
+
"state": "active",
|
|
27
|
+
"createdAt": "2026-01-15T14:00:00.000Z",
|
|
28
|
+
"modifiedAt": "2026-01-15T14:00:00.000Z",
|
|
29
|
+
"observedAt": "2026-01-15T14:00:00.000Z"
|
|
30
|
+
}
|
|
31
|
+
}
|