@reventlessdev/reventless-core 3.0.0-alpha.146 → 3.0.0-alpha.148

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
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.148 (2026-07-09)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **reventless-core:** DCB StateChangeSlices expose one mutation per command constructor ([136fe93](https://github.com/ReventlessDev/reventless-core/commit/136fe93c56c2e5115eb60d792e9d5e9c07eecc2b))
11
+
12
+
13
+ # 3.0.0-alpha.147 (2026-07-08)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **reventless-core:** composite DCB slices can read back their own events ([4604a91](https://github.com/ReventlessDev/reventless-core/commit/4604a9159fb8bf59b2191ad69fee6613c7f75cd9))
18
+
19
+
6
20
  # 3.0.0-alpha.146 (2026-07-08)
7
21
 
8
22
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.146",
3
+ "version": "3.0.0-alpha.148",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -28,17 +28,17 @@
28
28
  "dependencies": {
29
29
  "sury": "11.0.0-alpha.4",
30
30
  "uuid": "^13.0.0",
31
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
31
32
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.10",
32
33
  "@reventlessdev/rescript-fast-csv": "1.2.0-alpha.11",
33
- "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
34
34
  "@reventlessdev/rescript-jest": "1.0.0-alpha.6",
35
35
  "@reventlessdev/rescript-node-streams": "1.1.0-alpha.10",
36
36
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
37
37
  "@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
38
38
  "@reventlessdev/reventless-infra": "3.0.0-alpha.91",
39
39
  "@reventlessdev/reventless-interop": "3.0.0-alpha.24",
40
- "@reventlessdev/reventless-spec": "3.0.0-alpha.69",
41
- "@reventlessdev/rescript-ssh2": "1.1.0-alpha.11"
40
+ "@reventlessdev/rescript-ssh2": "1.1.0-alpha.11",
41
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.69"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "^12.3.0",
@@ -1,3 +1,5 @@
1
+ open ReventlessInfra.Api
2
+
1
3
  type queryNames = {
2
4
  singleFieldName: string,
3
5
  listFieldName: string,
@@ -39,6 +41,46 @@ let aggregateMutationField = (~plugin: string, ~aggregate: string, ~command: str
39
41
 
40
42
  let sliceMutationField = (~plugin: string, ~slice: string) => `${plugin}_${slice}`
41
43
 
44
+ // DCB StateChangeSlice mutation fields — one per API-exposed command constructor,
45
+ // mirroring the aggregate path (which emits one field per constructor). A slice whose
46
+ // command union has a single API-exposed constructor keeps the byte-identical
47
+ // `Plugin_Slice` name (no schema churn); a multi-command slice uses aggregate-style
48
+ // `Plugin_Slice_Ctor` per constructor so the SDL generator and command resolvers
49
+ // derive the constructor from the trailing `_` segment. @noApi variants are filtered
50
+ // out. Returns [(fieldName, constructorName)] in declaration order.
51
+ let sliceMutationFields = (
52
+ ~plugin: string,
53
+ ~slice: string,
54
+ ~commandSchema: S.t<unknown>,
55
+ ): array<(string, string)> => {
56
+ let allNames = Reventless.DcbTag.extractAllVariantNames(commandSchema->Obj.magic)
57
+ let filtered = ApiNoApiHelpers.filterNoApiVariants(allNames, commandSchema)
58
+ switch filtered {
59
+ | [] => []
60
+ | [single] => [(sliceMutationField(~plugin, ~slice), single)]
61
+ | _ =>
62
+ filtered->Array.map(ctor => (
63
+ aggregateMutationField(~plugin, ~aggregate=slice, ~command=ctor),
64
+ ctor,
65
+ ))
66
+ }
67
+ }
68
+
69
+ // The mutation field name for a single command constructor of a DCB StateChangeSlice,
70
+ // consistent with `sliceMutationFields`: single-command slices resolve every variant
71
+ // to the slice name, multi-command slices to the aggregate-style per-constructor name.
72
+ let sliceMutationFieldFor = (
73
+ ~plugin: string,
74
+ ~slice: string,
75
+ ~commandSchema: S.t<unknown>,
76
+ ~variant: string,
77
+ ): string =>
78
+ if sliceMutationFields(~plugin, ~slice, ~commandSchema)->Array.length <= 1 {
79
+ sliceMutationField(~plugin, ~slice)
80
+ } else {
81
+ aggregateMutationField(~plugin, ~aggregate=slice, ~command=variant)
82
+ }
83
+
42
84
  // Pluralize via the singular stem so already-plural names normalise correctly:
43
85
  // "Orders" → "Order" → "Orders" (was "Orderses"); "Categories" → "Category" →
44
86
  // "Categories" (was "Categorieses"). For singular entity names like
@@ -1,5 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
4
+ import * as ApiNoApiHelpers$ReventlessCore from "./ApiNoApiHelpers.res.mjs";
3
5
 
4
6
  function pluralize(n) {
5
7
  if (/[^aeiou]y$/i.test(n)) {
@@ -39,6 +41,35 @@ function sliceMutationField(plugin, slice) {
39
41
  return plugin + `_` + slice;
40
42
  }
41
43
 
44
+ function sliceMutationFields(plugin, slice, commandSchema) {
45
+ let allNames = DcbTag$Reventless.extractAllVariantNames(commandSchema);
46
+ let filtered = ApiNoApiHelpers$ReventlessCore.filterNoApiVariants(allNames, commandSchema);
47
+ let len = filtered.length;
48
+ if (len !== 1) {
49
+ if (len !== 0) {
50
+ return filtered.map(ctor => [
51
+ aggregateMutationField(plugin, slice, ctor),
52
+ ctor
53
+ ]);
54
+ } else {
55
+ return [];
56
+ }
57
+ }
58
+ let single = filtered[0];
59
+ return [[
60
+ sliceMutationField(plugin, slice),
61
+ single
62
+ ]];
63
+ }
64
+
65
+ function sliceMutationFieldFor(plugin, slice, commandSchema, variant) {
66
+ if (sliceMutationFields(plugin, slice, commandSchema).length <= 1) {
67
+ return sliceMutationField(plugin, slice);
68
+ } else {
69
+ return aggregateMutationField(plugin, slice, variant);
70
+ }
71
+ }
72
+
42
73
  function canonicalPlural(n) {
43
74
  return pluralize(singularize(n));
44
75
  }
@@ -94,10 +125,12 @@ export {
94
125
  stripViewSuffix,
95
126
  aggregateMutationField,
96
127
  sliceMutationField,
128
+ sliceMutationFields,
129
+ sliceMutationFieldFor,
97
130
  canonicalPlural,
98
131
  queryFieldNamesForReadModel,
99
132
  queryFieldNamesForStateView,
100
133
  queryFieldNamesForSliceQueryDb,
101
134
  adminField,
102
135
  }
103
- /* No side effect */
136
+ /* DcbTag-Reventless Not a pure module */
@@ -410,7 +410,11 @@ module Make = (
410
410
  if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
411
411
  registerResolver(
412
412
  ~kind=Dcb,
413
- ~fields=[Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)],
413
+ ~fields=Api_Naming.sliceMutationFields(
414
+ ~plugin=name,
415
+ ~slice=S.Spec.name,
416
+ ~commandSchema,
417
+ )->Array.map(((f, _)) => f),
414
418
  ~commandSchema,
415
419
  ~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
416
420
  )
@@ -423,7 +427,11 @@ module Make = (
423
427
  if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
424
428
  registerResolver(
425
429
  ~kind=Dcb,
426
- ~fields=[Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)],
430
+ ~fields=Api_Naming.sliceMutationFields(
431
+ ~plugin=name,
432
+ ~slice=S.Spec.name,
433
+ ~commandSchema,
434
+ )->Array.map(((f, _)) => f),
427
435
  ~commandSchema,
428
436
  ~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
429
437
  )
@@ -446,7 +454,8 @@ module Make = (
446
454
  ) => {
447
455
  let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
448
456
  if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
449
- let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
457
+ // One generateCommand backs every constructor's field — the TAG is
458
+ // injected by the resolver per field, so a single binding suffices.
450
459
  let generateCommand = CommandGenerator_Callback.makeGenerateCommand(
451
460
  ~publishJsons=ops.publishJsons,
452
461
  ~publishJsonsAndWait=?ops.publishJsonsAndWait,
@@ -455,7 +464,8 @@ module Make = (
455
464
  ~componentKind=CommandGenerator_Callback.StateChangeSlice,
456
465
  ~stripIdFromParams=false,
457
466
  )
458
- bindHandler(~field=fieldName, ~generateCommand)
467
+ Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
468
+ ->Array.forEach(((fieldName, _)) => bindHandler(~field=fieldName, ~generateCommand))
459
469
  }
460
470
  })
461
471
  })
@@ -471,7 +481,8 @@ module Make = (
471
481
  ) => {
472
482
  let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
473
483
  if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
474
- let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
484
+ // One generateCommand backs every constructor's field — the TAG is
485
+ // injected by the resolver per field, so a single binding suffices.
475
486
  let generateCommand = CommandGenerator_Callback.makeGenerateCommand(
476
487
  ~publishJsons=asyncOps.publishJsons,
477
488
  ~publishJsonsAndWait=?asyncOps.publishJsonsAndWait,
@@ -480,7 +491,8 @@ module Make = (
480
491
  ~componentKind=CommandGenerator_Callback.StateChangeSlice,
481
492
  ~stripIdFromParams=false,
482
493
  )
483
- bindHandler(~field=fieldName, ~generateCommand)
494
+ Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
495
+ ->Array.forEach(((fieldName, _)) => bindHandler(~field=fieldName, ~generateCommand))
484
496
  }
485
497
  })
486
498
  })
@@ -706,19 +718,19 @@ module Make = (
706
718
 
707
719
  // Collect DCB mutation field names + TAGs for AppSync resolver creation
708
720
  // (excludes @noApi slices — includes both sync and async)
721
+ // One (fieldName, TAG) pair per API-exposed command constructor. The AWS
722
+ // resolver layer (CommandGeneratorResolvers_AppSync.makeDcb) zips these into
723
+ // one resolver per field, each injecting its own constructor TAG into the
724
+ // Lambda payload — so a multi-command slice's every command is dispatchable.
709
725
  let dcbMutationData =
710
- stateChangeSlices->Array.filterMap((
726
+ stateChangeSlices->Array.flatMap((
711
727
  module(S: StateChangeSlice.T),
712
728
  ) => {
713
729
  let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
714
730
  if ApiNoApiHelpers.isNoApi(commandSchema) {
715
- None
731
+ []
716
732
  } else {
717
- let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
718
- let constructorNames =
719
- Reventless.DcbTag.extractAllVariantNames(S.Spec.commandSchema->Obj.magic)
720
- let tag = constructorNames->Array.get(0)->Option.getOr(S.Spec.name)
721
- Some((fieldName, tag))
733
+ Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
722
734
  }
723
735
  })
724
736
  let dcbFieldNames = dcbMutationData->Array.map(((f, _)) => f)
@@ -812,24 +824,29 @@ module Make = (
812
824
  module(S: StateChangeSlice.T),
813
825
  ) => {
814
826
  let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
815
- if ApiNoApiHelpers.isNoApi(commandSchema) {
827
+ let fieldSpecs =
828
+ Api_Naming.sliceMutationFields(~plugin=name, ~slice=S.Spec.name, ~commandSchema)
829
+ if ApiNoApiHelpers.isNoApi(commandSchema) || fieldSpecs->Array.length == 0 {
816
830
  None
817
831
  } else {
818
832
  let sliceDef =
819
833
  pluginStructure->Option.flatMap(s =>
820
834
  s.stateChangeSlices->Array.find(d => d.name == S.Spec.name)
821
835
  )
822
- let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
836
+ // One SDL field per constructor, each with its own constructor's args and
837
+ // its own per-constructor authorization rule.
838
+ let commandAuthorization: unknown => Reventless.Authorization.permission =
839
+ S.Spec.commandAuthorization->Obj.magic
823
840
  let fieldPermissions = Dict.make()
824
- switch permissionForFirstConstructor(
825
- ~commandSchema,
826
- ~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
827
- ) {
828
- | Some(rule) => fieldPermissions->Dict.set(fieldName, rule)
829
- | None => ()
830
- }
841
+ fieldSpecs->Array.forEach(((fieldName, ctor)) => {
842
+ let hasPayload =
843
+ Reventless.DcbTag.isVariantPayloadBearing(commandSchema->Obj.magic, ctor)
844
+ let syntheticCmd: unknown =
845
+ hasPayload ? {"TAG": ctor}->Obj.magic : ctor->Obj.magic
846
+ fieldPermissions->Dict.set(fieldName, commandAuthorization(syntheticCmd))
847
+ })
831
848
  Some({
832
- ReventlessInfra.Api.fieldNames: [fieldName],
849
+ ReventlessInfra.Api.fieldNames: fieldSpecs->Array.map(((f, _)) => f),
833
850
  commandSchema,
834
851
  fieldPermissions,
835
852
  systemCallable: systemCallableComponents->Array.includes(S.Spec.name),
@@ -225,13 +225,13 @@ function Make(DcbEventLogStorage) {
225
225
  syncSlices.forEach(S => {
226
226
  let commandSchema = S.Spec.commandSchema;
227
227
  if (!ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
228
- return registerResolver("Dcb", [Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name)], commandSchema, S.Spec.commandAuthorization);
228
+ return registerResolver("Dcb", Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).map(param => param[0]), commandSchema, S.Spec.commandAuthorization);
229
229
  }
230
230
  });
231
231
  asyncSlices.forEach(S => {
232
232
  let commandSchema = S.Spec.commandSchema;
233
233
  if (!ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
234
- return registerResolver("Dcb", [Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name)], commandSchema, S.Spec.commandAuthorization);
234
+ return registerResolver("Dcb", Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).map(param => param[0]), commandSchema, S.Spec.commandAuthorization);
235
235
  }
236
236
  });
237
237
  }
@@ -243,9 +243,8 @@ function Make(DcbEventLogStorage) {
243
243
  if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
244
244
  return;
245
245
  }
246
- let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name);
247
246
  let generateCommand = CommandGenerator_Callback$ReventlessCore.makeGenerateCommand(ops.publishJsons, ops.publishJsonsAndWait, S.Spec.name, S.Spec.commandSchema, "StateChangeSlice", false);
248
- bindHandler(fieldName, generateCommand);
247
+ Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).forEach(param => bindHandler(param[0], generateCommand));
249
248
  });
250
249
  });
251
250
  if (asyncDcbCommandTopicOpt !== undefined) {
@@ -255,9 +254,8 @@ function Make(DcbEventLogStorage) {
255
254
  if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
256
255
  return;
257
256
  }
258
- let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name);
259
257
  let generateCommand = CommandGenerator_Callback$ReventlessCore.makeGenerateCommand(asyncOps.publishJsons, asyncOps.publishJsonsAndWait, S.Spec.name, S.Spec.commandSchema, "StateChangeSlice", false);
260
- bindHandler(fieldName, generateCommand);
258
+ Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema).forEach(param => bindHandler(param[0], generateCommand));
261
259
  });
262
260
  });
263
261
  }
@@ -394,18 +392,13 @@ function Make(DcbEventLogStorage) {
394
392
  let asyncDcbResources = Object.values(asyncStateChangeSlicesOutputs).flatMap(outputs => outputs.resources);
395
393
  let inboundFieldNames = inboundTranslationSliceData.map(param => param[1]);
396
394
  let inboundSchemas = inboundTranslationSliceData.map(param => param[3]);
397
- let dcbMutationData = Stdlib_Array.filterMap(stateChangeSlices, S => {
395
+ let dcbMutationData = stateChangeSlices.flatMap(S => {
398
396
  let commandSchema = S.Spec.commandSchema;
399
397
  if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
400
- return;
398
+ return [];
399
+ } else {
400
+ return Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema);
401
401
  }
402
- let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name);
403
- let constructorNames = DcbTag$Reventless.extractAllVariantNames(S.Spec.commandSchema);
404
- let tag = Stdlib_Option.getOr(constructorNames[0], S.Spec.name);
405
- return [
406
- fieldName,
407
- tag
408
- ];
409
402
  });
410
403
  let dcbFieldNames = dcbMutationData.map(param => param[0]);
411
404
  let dcbTags = dcbMutationData.map(param => param[1]);
@@ -458,18 +451,23 @@ function Make(DcbEventLogStorage) {
458
451
  };
459
452
  let mutationEntriesFromSlices = Stdlib_Array.filterMap(stateChangeSlices, S => {
460
453
  let commandSchema = S.Spec.commandSchema;
461
- if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
454
+ let fieldSpecs = Api_Naming$ReventlessCore.sliceMutationFields(name, S.Spec.name, commandSchema);
455
+ if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema) || fieldSpecs.length === 0) {
462
456
  return;
463
457
  }
464
458
  let sliceDef = Stdlib_Option.flatMap(pluginStructure, s => s.stateChangeSlices.find(d => d.name === S.Spec.name));
465
- let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, S.Spec.name);
459
+ let commandAuthorization = S.Spec.commandAuthorization;
466
460
  let fieldPermissions = {};
467
- let rule = permissionForFirstConstructor(commandSchema, S.Spec.commandAuthorization);
468
- if (rule !== undefined) {
469
- fieldPermissions[fieldName] = rule;
470
- }
461
+ fieldSpecs.forEach(param => {
462
+ let ctor = param[1];
463
+ let hasPayload = DcbTag$Reventless.isVariantPayloadBearing(commandSchema, ctor);
464
+ let syntheticCmd = hasPayload ? ({
465
+ TAG: ctor
466
+ }) : ctor;
467
+ fieldPermissions[param[0]] = commandAuthorization(syntheticCmd);
468
+ });
471
469
  return {
472
- fieldNames: [fieldName],
470
+ fieldNames: fieldSpecs.map(param => param[0]),
473
471
  commandSchema: commandSchema,
474
472
  fieldPermissions: fieldPermissions,
475
473
  systemCallable: systemCallableComponents.includes(S.Spec.name),
@@ -61,8 +61,7 @@ module Make = (
61
61
  | Some(keys) => baseTags->Array.filter(t => keys->Array.includes(t.key))
62
62
  | None => baseTags
63
63
  }
64
- let tags =
65
- indexedTags->Array.concat([{Reventless.DcbTag.key: "originatorSlice", value: Spec.name}])
64
+ let tags = indexedTags
66
65
  // Inherit service from the triggering command — the DcbEventLog publish path
67
66
  // overrides service to `<name>DcbEventLog` for routing so EventCollector
68
67
  // subscriptions still match.
@@ -83,12 +82,8 @@ module Make = (
83
82
  // tag value(s) instead of logging no id.
84
83
  let readEventId = (tags: array<Reventless.DcbTag.tag>): option<string> => {
85
84
  let ownTagValues = () => {
86
- // Skip the `originatorSlice` metadata tag that `encodeEvent` appends to
87
- // every stored event — it's the producing slice's name, not an entity id.
88
85
  let vals =
89
- tags
90
- ->Array.filter((t: Reventless.DcbTag.tag) => t.key != "originatorSlice")
91
- ->Array.reduce([], (acc, t: Reventless.DcbTag.tag) =>
86
+ tags->Array.reduce([], (acc, t: Reventless.DcbTag.tag) =>
92
87
  acc->Array.includes(t.value) ? acc : acc->Array.concat([t.value])
93
88
  )
94
89
  vals->Array.length == 0 ? None : Some(vals->Array.join(","))
@@ -29,7 +29,7 @@ function Make(Spec) {
29
29
  ]]);
30
30
  let readEventId = tags => {
31
31
  let ownTagValues = () => {
32
- let vals = Stdlib_Array.reduce(tags.filter(t => t.key !== "originatorSlice"), [], (acc, t) => {
32
+ let vals = Stdlib_Array.reduce(tags, [], (acc, t) => {
33
33
  if (acc.includes(t.value)) {
34
34
  return acc;
35
35
  } else {
@@ -188,15 +188,11 @@ function Make(Spec) {
188
188
  let baseTags = DcbTag$Reventless.extractTagsExpanded(Spec.eventSchema, e);
189
189
  let keys = tagKeysByEventType[eventType];
190
190
  let indexedTags = keys !== undefined ? baseTags.filter(t => keys.includes(t.key)) : baseTags;
191
- let tags = indexedTags.concat([{
192
- key: "originatorSlice",
193
- value: Spec.name
194
- }]);
195
191
  let meta = Message$ReventlessCore.deriveMeta(parentMeta, undefined);
196
192
  return {
197
193
  eventType: eventType,
198
194
  data: match[1],
199
- tags: tags,
195
+ tags: indexedTags,
200
196
  meta: meta
201
197
  };
202
198
  });
@@ -440,7 +440,13 @@ let make = (
440
440
  Reventless.Plugin.name: SCS.Spec.name,
441
441
  commands: extractCommandDefs(
442
442
  ~isAggregate=false,
443
- ~mutationFieldFor=_variantName => Api_Naming.sliceMutationField(~plugin=name, ~slice=SCS.Spec.name),
443
+ ~mutationFieldFor=variantName =>
444
+ Api_Naming.sliceMutationFieldFor(
445
+ ~plugin=name,
446
+ ~slice=SCS.Spec.name,
447
+ ~commandSchema=SCS.Spec.commandSchema->S.castToUnknown,
448
+ ~variant=variantName,
449
+ ),
444
450
  SCS.Spec.commandSchema->S.castToUnknown,
445
451
  ),
446
452
  producedEventTypes: produced,
@@ -361,7 +361,7 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
361
361
  let consumed = match$1[1];
362
362
  return {
363
363
  name: SCS.Spec.name,
364
- commands: extractCommandDefs(false, _variantName => Api_Naming$ReventlessCore.sliceMutationField(name, SCS.Spec.name), SCS.Spec.commandSchema),
364
+ commands: extractCommandDefs(false, variantName => Api_Naming$ReventlessCore.sliceMutationFieldFor(name, SCS.Spec.name, SCS.Spec.commandSchema, variantName), SCS.Spec.commandSchema),
365
365
  producedEventTypes: produced,
366
366
  consumedEventTypes: consumed,
367
367
  linkedViews: linkedSvsFor(produced),
@@ -61,8 +61,7 @@ let sourceCFields = (
61
61
  // Pushed via AppSync Events API. Admin / observability only.
62
62
  //
63
63
  // Returns (subscriptionFields, supportingTypes) — the supporting type is the
64
- // {DisplayName}EventLogEvent type that carries position, eventType, payload, and
65
- // the optional originatorSlice tag injected by StateChangeSlice_Callback.
64
+ // {DisplayName}EventLogEvent type that carries position, eventType, and payload.
66
65
 
67
66
  let sourceAFieldsAndTypes = (
68
67
  ~eventLogEntries: array<eventLogSchemaEntry>,
@@ -76,7 +75,7 @@ let sourceAFieldsAndTypes = (
76
75
  seen->Set.add(n)
77
76
  fields->Array.push(` on${n}EventLog_eventAppended: ${n}EventLogEvent`)
78
77
  types->Array.push(
79
- `type ${n}EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n originatorSlice: String\n}`,
78
+ `type ${n}EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n}`,
80
79
  )
81
80
  }
82
81
  })
@@ -41,7 +41,7 @@ function sourceAFieldsAndTypes(eventLogEntries) {
41
41
  if (!seen.has(n)) {
42
42
  seen.add(n);
43
43
  fields.push(` on` + n + `EventLog_eventAppended: ` + n + `EventLogEvent`);
44
- types.push(`type ` + n + `EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n originatorSlice: String\n}`);
44
+ types.push(`type ` + n + `EventLogEvent {\n position: String!\n eventType: String!\n payload: AWSJSON!\n}`);
45
45
  return;
46
46
  }
47
47
  });
@@ -81,7 +81,7 @@ describe("StateChangeSlice_Callback:", () => {
81
81
  ))->toEqual(([Ok("ref-1")], 1, 1))
82
82
  })
83
83
 
84
- testPromise("stored event has correct tags", async () => {
84
+ testPromise("stored event carries only its entity tags (no framework provenance tag)", async () => {
85
85
  let _ = await TestHandler.handleCommands(
86
86
  testDcbEventLog,
87
87
  Stream.fromIterable([
@@ -90,10 +90,11 @@ describe("StateChangeSlice_Callback:", () => {
90
90
  )->Effect.runPromise
91
91
  let storedEvent = mock.getEvents()->Array.getUnsafe(0)
92
92
 
93
- expect(storedEvent.tags)->toEqual([
94
- {Reventless.DcbTag.key: "itemId", value: "item-1"},
95
- {Reventless.DcbTag.key: "originatorSlice", value: "TestStateChangeSlice"},
96
- ])
93
+ // No `originatorSlice` (or any other) metadata tag is smuggled into the tag
94
+ // set — a stored event's tags are exactly its DCB entity tags, so the
95
+ // composite read/write keys stay aligned. See
96
+ // docs/analysis/dcb-event-provenance-and-metadata.md.
97
+ expect(storedEvent.tags)->toEqual([{Reventless.DcbTag.key: "itemId", value: "item-1"}])
97
98
  })
98
99
  })
99
100
 
@@ -201,23 +201,17 @@ globalThis.describe("StateChangeSlice_Callback:", () => {
201
201
  1
202
202
  ]);
203
203
  });
204
- globalThis.test("stored event has correct tags", async () => {
204
+ globalThis.test("stored event carries only its entity tags (no framework provenance tag)", async () => {
205
205
  await Effect.runPromise(TestHandler.handleCommands(undefined, undefined, testDcbEventLog, Stream.fromIterable([makeTopicItem("ref-1", {
206
206
  TAG: "CreateItem",
207
207
  itemId: "item-1",
208
208
  name: "Test"
209
209
  })])));
210
210
  let storedEvent = mock.getEvents()[0];
211
- globalThis.expect(storedEvent.tags).toEqual([
212
- {
211
+ globalThis.expect(storedEvent.tags).toEqual([{
213
212
  key: "itemId",
214
213
  value: "item-1"
215
- },
216
- {
217
- key: "originatorSlice",
218
- value: "TestStateChangeSlice"
219
- }
220
- ]);
214
+ }]);
221
215
  });
222
216
  });
223
217
  globalThis.describe("handleCommands - decide returns Ok([])", () => {