@cratis/pi 0.0.1

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 (125) hide show
  1. package/README.md +43 -0
  2. package/package.json +24 -0
  3. package/skills/cratis-application-react-specifications/LICENSE +2 -0
  4. package/skills/cratis-application-react-specifications/SKILL.md +192 -0
  5. package/skills/cratis-application-slice-specifications/LICENSE +2 -0
  6. package/skills/cratis-application-slice-specifications/SKILL.md +222 -0
  7. package/skills/cratis-arc-authentication-authorization-and-identity/LICENSE +2 -0
  8. package/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +233 -0
  9. package/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +140 -0
  10. package/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +112 -0
  11. package/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +102 -0
  12. package/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +85 -0
  13. package/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +87 -0
  14. package/skills/cratis-arc-command/LICENSE +2 -0
  15. package/skills/cratis-arc-command/SKILL.md +286 -0
  16. package/skills/cratis-arc-command/references/command-result.md +111 -0
  17. package/skills/cratis-arc-command/references/handler-shapes.md +135 -0
  18. package/skills/cratis-arc-command/references/proxy-generation.md +87 -0
  19. package/skills/cratis-arc-command/references/read-model-injection.md +97 -0
  20. package/skills/cratis-arc-command-execution/LICENSE +2 -0
  21. package/skills/cratis-arc-command-execution/SKILL.md +148 -0
  22. package/skills/cratis-arc-command-validation/LICENSE +2 -0
  23. package/skills/cratis-arc-command-validation/SKILL.md +230 -0
  24. package/skills/cratis-arc-ef-core-migration/LICENSE +2 -0
  25. package/skills/cratis-arc-ef-core-migration/SKILL.md +231 -0
  26. package/skills/cratis-arc-observable-query-http/LICENSE +2 -0
  27. package/skills/cratis-arc-observable-query-http/SKILL.md +157 -0
  28. package/skills/cratis-arc-query-paging/LICENSE +2 -0
  29. package/skills/cratis-arc-query-paging/SKILL.md +173 -0
  30. package/skills/cratis-arc-react-page/LICENSE +2 -0
  31. package/skills/cratis-arc-react-page/SKILL.md +291 -0
  32. package/skills/cratis-arc-react-page/references/data-page.md +125 -0
  33. package/skills/cratis-arc-react-page/references/data-tables.md +136 -0
  34. package/skills/cratis-arc-react-page/references/dialogs.md +238 -0
  35. package/skills/cratis-arc-react-page/references/mvvm.md +150 -0
  36. package/skills/cratis-arc-react-page/references/queries-and-commands.md +200 -0
  37. package/skills/cratis-chronicle-cli-operations/LICENSE +2 -0
  38. package/skills/cratis-chronicle-cli-operations/SKILL.md +199 -0
  39. package/skills/cratis-chronicle-client-dotnet/LICENSE +2 -0
  40. package/skills/cratis-chronicle-client-dotnet/SKILL.md +433 -0
  41. package/skills/cratis-chronicle-client-elixir/LICENSE +2 -0
  42. package/skills/cratis-chronicle-client-elixir/SKILL.md +429 -0
  43. package/skills/cratis-chronicle-client-kotlin/LICENSE +2 -0
  44. package/skills/cratis-chronicle-client-kotlin/SKILL.md +498 -0
  45. package/skills/cratis-chronicle-client-typescript/LICENSE +2 -0
  46. package/skills/cratis-chronicle-client-typescript/SKILL.md +396 -0
  47. package/skills/cratis-chronicle-compliance/LICENSE +2 -0
  48. package/skills/cratis-chronicle-compliance/SKILL.md +246 -0
  49. package/skills/cratis-chronicle-event-constraints/LICENSE +2 -0
  50. package/skills/cratis-chronicle-event-constraints/SKILL.md +227 -0
  51. package/skills/cratis-chronicle-event-modeling/LICENSE +2 -0
  52. package/skills/cratis-chronicle-event-modeling/SKILL.md +157 -0
  53. package/skills/cratis-chronicle-event-specifications/LICENSE +2 -0
  54. package/skills/cratis-chronicle-event-specifications/SKILL.md +200 -0
  55. package/skills/cratis-chronicle-event-type-migration/LICENSE +2 -0
  56. package/skills/cratis-chronicle-event-type-migration/SKILL.md +235 -0
  57. package/skills/cratis-chronicle-mcp-inspection/LICENSE +2 -0
  58. package/skills/cratis-chronicle-mcp-inspection/SKILL.md +79 -0
  59. package/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +12 -0
  60. package/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +8 -0
  61. package/skills/cratis-chronicle-multi-tenancy/LICENSE +2 -0
  62. package/skills/cratis-chronicle-multi-tenancy/SKILL.md +198 -0
  63. package/skills/cratis-chronicle-projection/LICENSE +2 -0
  64. package/skills/cratis-chronicle-projection/SKILL.md +162 -0
  65. package/skills/cratis-chronicle-projection/references/fluent-builder.md +126 -0
  66. package/skills/cratis-chronicle-projection/references/model-bound-attributes.md +118 -0
  67. package/skills/cratis-chronicle-reactor/LICENSE +2 -0
  68. package/skills/cratis-chronicle-reactor/SKILL.md +344 -0
  69. package/skills/cratis-chronicle-read-model/LICENSE +2 -0
  70. package/skills/cratis-chronicle-read-model/SKILL.md +193 -0
  71. package/skills/cratis-chronicle-read-model/references/queries.md +142 -0
  72. package/skills/cratis-chronicle-read-model-specifications/LICENSE +2 -0
  73. package/skills/cratis-chronicle-read-model-specifications/SKILL.md +176 -0
  74. package/skills/cratis-chronicle-reducer/LICENSE +2 -0
  75. package/skills/cratis-chronicle-reducer/SKILL.md +173 -0
  76. package/skills/cratis-chronicle-web-workbench/LICENSE +2 -0
  77. package/skills/cratis-chronicle-web-workbench/SKILL.md +177 -0
  78. package/skills/cratis-cli-terminal-workbench/LICENSE +2 -0
  79. package/skills/cratis-cli-terminal-workbench/SKILL.md +156 -0
  80. package/skills/cratis-code-review/LICENSE +2 -0
  81. package/skills/cratis-code-review/SKILL.md +177 -0
  82. package/skills/cratis-components-accessibility/LICENSE +2 -0
  83. package/skills/cratis-components-accessibility/SKILL.md +176 -0
  84. package/skills/cratis-components-schema-editor/LICENSE +2 -0
  85. package/skills/cratis-components-schema-editor/SKILL.md +238 -0
  86. package/skills/cratis-components-stepper-command-dialog/LICENSE +2 -0
  87. package/skills/cratis-components-stepper-command-dialog/SKILL.md +209 -0
  88. package/skills/cratis-components-styling/LICENSE +2 -0
  89. package/skills/cratis-components-styling/SKILL.md +221 -0
  90. package/skills/cratis-components-toolbar/LICENSE +2 -0
  91. package/skills/cratis-components-toolbar/SKILL.md +259 -0
  92. package/skills/cratis-documentation-writing/LICENSE +2 -0
  93. package/skills/cratis-documentation-writing/SKILL.md +121 -0
  94. package/skills/cratis-event-model-diagram/LICENSE +2 -0
  95. package/skills/cratis-event-model-diagram/SKILL.md +142 -0
  96. package/skills/cratis-fundamentals-concept/LICENSE +2 -0
  97. package/skills/cratis-fundamentals-concept/SKILL.md +198 -0
  98. package/skills/cratis-fundamentals-type-discovery/LICENSE +2 -0
  99. package/skills/cratis-fundamentals-type-discovery/SKILL.md +152 -0
  100. package/skills/cratis-governed-release-methodology/LICENSE +2 -0
  101. package/skills/cratis-governed-release-methodology/SKILL.md +297 -0
  102. package/skills/cratis-lens-browser-extension/LICENSE +2 -0
  103. package/skills/cratis-lens-browser-extension/SKILL.md +239 -0
  104. package/skills/cratis-performance-review/LICENSE +2 -0
  105. package/skills/cratis-performance-review/SKILL.md +139 -0
  106. package/skills/cratis-screenplay-model-authoring/LICENSE +2 -0
  107. package/skills/cratis-screenplay-model-authoring/SKILL.md +231 -0
  108. package/skills/cratis-security-review/LICENSE +2 -0
  109. package/skills/cratis-security-review/SKILL.md +141 -0
  110. package/skills/cratis-specification-by-example/LICENSE +2 -0
  111. package/skills/cratis-specification-by-example/SKILL.md +188 -0
  112. package/skills/cratis-specifications-csharp/LICENSE +2 -0
  113. package/skills/cratis-specifications-csharp/SKILL.md +230 -0
  114. package/skills/cratis-specifications-csharp/references/application-scenarios.md +191 -0
  115. package/skills/cratis-specifications-csharp/references/csharp-patterns.md +142 -0
  116. package/skills/cratis-specifications-csharp/references/integration-specs.md +126 -0
  117. package/skills/cratis-specifications-typescript/LICENSE +2 -0
  118. package/skills/cratis-specifications-typescript/SKILL.md +202 -0
  119. package/skills/cratis-specifications-typescript/references/typescript-patterns.md +168 -0
  120. package/skills/cratis-stage-rendering-and-sandbox/LICENSE +2 -0
  121. package/skills/cratis-stage-rendering-and-sandbox/SKILL.md +230 -0
  122. package/skills/cratis-studio-mcp-safety-guidance/LICENSE +2 -0
  123. package/skills/cratis-studio-mcp-safety-guidance/SKILL.md +69 -0
  124. package/skills/cratis-studio-mcp-safety-guidance/references/blocked-tools.md +12 -0
  125. package/skills/cratis-studio-mcp-safety-guidance/references/observational-tools.md +8 -0
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: cratis-chronicle-projection
3
+ description: Add Chronicle projection behavior to an existing read model - model-bound attributes first, the fluent IProjectionFor<T> builder when they cannot express the shape. Covers AutoMap, keys, children and nested types, counters, event-sequence selection, and the startup-crash traps. Use when populating a read model from events; do not use to create the read model and its query surface, and do not reach for a reducer before exhausting these options.
4
+ license: MIT
5
+ ---
6
+
7
+ # Chronicle projections
8
+
9
+ A projection consumes **events** and produces read-model state. It is pure: no
10
+ side effects, and it never reads another read model. Prefer model-bound
11
+ attributes on the read model; fall back to the fluent builder only when the
12
+ attributes cannot express the shape; reach for a reducer only after both fail.
13
+
14
+ ## Verified product sources
15
+
16
+ This skill is verified against this exact source:
17
+
18
+ | Package | Version | Purpose |
19
+ | --- | --- | --- |
20
+ | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Projections`, `Cratis.Chronicle.Projections.ModelBound`, `Cratis.Chronicle.Keys`, `Cratis.Chronicle.EventSequences` |
21
+
22
+ Reverify product sources before claiming support for another version.
23
+
24
+ > `[ReadModel]` and `[Path]` are **Arc** types (`Cratis.Arc.Queries.ModelBound`),
25
+ > not Chronicle types. A Chronicle read model that exists only as a projection
26
+ > target needs no `[ReadModel]` at all — that attribute is what gives it an Arc
27
+ > query surface.
28
+
29
+ ## Model-bound projections — the default
30
+
31
+ Put the projection on the read model with attributes. No separate class.
32
+
33
+ ```csharp
34
+ using Cratis.Chronicle.Keys;
35
+ using Cratis.Chronicle.Projections.ModelBound;
36
+
37
+ [FromEvent<<EventName>>]
38
+ public record <ReadModelName>(
39
+ [property: Key] <IdType> Id,
40
+ <PropertyType> <PropertyName>);
41
+ ```
42
+
43
+ Matching property names map automatically. **AutoMap is on by default** — both
44
+ for model-bound types and for the fluent builder — so never call `.AutoMap()`
45
+ except inside a scope where you disabled it.
46
+
47
+ `[Key]` is `Cratis.Chronicle.Keys.KeyAttribute`. It has no arguments and targets
48
+ a property or a record constructor parameter, never the class.
49
+
50
+ Read [references/model-bound-attributes.md](references/model-bound-attributes.md)
51
+ for the complete attribute list with exact arities, parameter names, and valid
52
+ targets. Two things there are worth knowing before you write anything:
53
+
54
+ - `[Nested]`, `[FromAll]`, and `[FromEvery]` are **non-generic**.
55
+ - `ConstantKey` on `[Count<T>]`, `[Increment<T>]`, `[Decrement<T>]`, and
56
+ `[FromEvent<T>]` is an **object-initializer property**, never a constructor
57
+ argument: `[Count<TEvent>(ConstantKey = "<key>")]`.
58
+
59
+ ## Fluent projections — the fallback
60
+
61
+ Use `Cratis.Chronicle.Projections.IProjectionFor<TReadModel>` when the attributes
62
+ cannot express the shape — for example when two events feed one child collection
63
+ through different key properties.
64
+
65
+ ```csharp
66
+ using Cratis.Chronicle.Projections;
67
+
68
+ public class <ReadModelName>Projection : IProjectionFor<<ReadModelName>>
69
+ {
70
+ public void Define(IProjectionBuilderFor<<ReadModelName>> builder) =>
71
+ builder
72
+ .From<<EventName>>(from => from.UsingKey(@event => @event.<KeyProperty>))
73
+ .RemovedWith<<RemovalEventName>>();
74
+ }
75
+ ```
76
+
77
+ `Define` returns `void`. **There is no `Identifier` or `ProjectionId` member on
78
+ the interface** — do not add one. Use `[Projection(id: "<id>")]` when the
79
+ identity must be explicit.
80
+
81
+ Read [references/fluent-builder.md](references/fluent-builder.md) for the real
82
+ member list. The trap that costs the most time: **`UsingKey`, `UsingParentKey`,
83
+ `UsingCompositeKey`, and `UsingConstantKey` are not on the projection builder.**
84
+ They live on the per-event builder that `From<TEvent>` hands to your callback.
85
+
86
+ ## Joins are on events, never on read models
87
+
88
+ Both `[Join<T>]` and the fluent `Join<TEvent>` take an **event** type. Joining on
89
+ a read model type is not supported and is not what the API means. If a read model
90
+ needs a field that only another read model has, the model is missing an event —
91
+ fix the event model rather than cross-reading at runtime.
92
+
93
+ ## Selecting the source
94
+
95
+ Class-level `[EventSequence("<name>")]`, `[EventLog]`, or `[EventStore("<name>")]`
96
+ choose where a model-bound projection reads from.
97
+
98
+ > **`[FromEventSequence]` no longer exists.** It was removed. The replacement on a
99
+ > model-bound read model is `[EventSequence("<name>")]` or `[EventLog]`. The
100
+ > fluent builder still has a `FromEventSequence(EventSequenceId)` **method** —
101
+ > that one is alive; do not confuse the two.
102
+
103
+ ## Projections cannot filter by appended metadata
104
+
105
+ Chronicle correlates appended metadata in two different ways:
106
+
107
+ - **Projections** select their input through event types, joins, and event
108
+ sequence configuration — and nothing else. A projection's definition has **no
109
+ field for a filter at all**.
110
+ - **Reducers and reactors** can additionally filter by appended tag, event source
111
+ type, and event stream type, using `[FilterEventsByTag]`, `[EventSourceType]`,
112
+ and `[EventStreamType]`.
113
+
114
+ `[Tag]` and `[Tags]` on a projection or read model label the artifact; they never
115
+ filter. If you need metadata-based selection, pair the projection with a reducer
116
+ or a reactor rather than annotating the projection.
117
+
118
+ ## Startup-crash traps
119
+
120
+ - **Duplicate class-level `[FromEvent<T>]`.** Declaring `[FromEvent<T>]` with no
121
+ `key:` on **both** a parent and a nested or child type for the **same** event
122
+ throws a duplicate-key exception at startup. Keep `[FromEvent<T>]` on the
123
+ nested type only, or switch the nested type to property-level `[SetFrom<T>]`.
124
+ - **Duplicate `[SetFromContext<T>]`.** Two properties with
125
+ `[SetFromContext<SameEvent>]` on one read model crash at startup. Merge them,
126
+ or use `[FromEvery]`.
127
+ - **Chaining after `AutoMap()`/`NoAutoMap()`.** Those two return the base builder
128
+ interface, so `.NotRewindable()`, `.Passive()`, `.ContainerName()`, and
129
+ `.FromEventSequence()` will not compile after them. Put them last.
130
+
131
+ ## `[FromAll]` versus `[FromEvery]`
132
+
133
+ - `[FromAll]` subscribes to **every** event type in the system. It is for audit
134
+ and log models; pair it with `[NotRewindable]`. It targets a **property only**,
135
+ so it cannot go on a record positional parameter.
136
+ - `[FromEvery]` captures across the events the model **already** declares through
137
+ its `[FromEvent<T>]` attributes — for example to stamp `EventContext` data. It
138
+ subscribes to nothing new.
139
+
140
+ Their two optional arguments are in **opposite order**: `[FromAll(contextProperty,
141
+ property)]` and `[FromEvery(property, contextProperty)]`. Name them.
142
+
143
+ ## After creating
144
+
145
+ Build. Fix every error before completing. Then drive the contributing events
146
+ through the read-model specification for this model and assert the projected
147
+ state — seed each contributing stream with its own event-source setup when the
148
+ projection spans streams.
149
+
150
+ ## Verify
151
+
152
+ - Model-bound attributes were exhausted before the fluent builder, and the
153
+ fluent builder before any reducer.
154
+ - `.AutoMap()` is called only inside a `.NoAutoMap()` scope.
155
+ - Every join is on an event type.
156
+ - No `[FromEventSequence]` attribute appears anywhere.
157
+ - No projection carries a filter attribute expecting it to filter.
158
+ - `ConstantKey` is written as a property initializer.
159
+ - The same event does not carry class-level `[FromEvent<T>]` on both a parent and
160
+ a nested or child type.
161
+ - The project builds clean and the read-model specifications pass against the
162
+ verified package version.
@@ -0,0 +1,126 @@
1
+ # The fluent projection builder
2
+
3
+ Verified against `Cratis.Chronicle` `16.45.2`, namespace
4
+ `Cratis.Chronicle.Projections`.
5
+
6
+ ## The interfaces
7
+
8
+ ```csharp
9
+ public interface IProjection;
10
+
11
+ public interface IProjectionFor<TReadModel> : IProjection
12
+ where TReadModel : class
13
+ {
14
+ void Define(IProjectionBuilderFor<TReadModel> builder);
15
+ }
16
+ ```
17
+
18
+ `IProjection` is an empty marker. `Define` returns `void`. **The interface has
19
+ no `Identifier` or `ProjectionId` member** — declaring one adds a plain class
20
+ member that Chronicle does not read. Use
21
+ `[Projection(id: "<id>", eventSequence: "<name>")]` when the identity or source
22
+ sequence must be explicit.
23
+
24
+ ## `IProjectionBuilderFor<TReadModel>` — its own four members
25
+
26
+ ```csharp
27
+ IProjectionBuilderFor<TReadModel> FromEventSequence(EventSequenceId eventSequenceId);
28
+ IProjectionBuilderFor<TReadModel> ContainerName(string containerName);
29
+ IProjectionBuilderFor<TReadModel> NotRewindable();
30
+ IProjectionBuilderFor<TReadModel> Passive();
31
+ ```
32
+
33
+ ## Inherited from `IProjectionBuilder<TReadModel, TBuilder>`
34
+
35
+ ```csharp
36
+ IProjectionBuilder<TReadModel, TBuilder> AutoMap(); // returns the BASE interface
37
+ IProjectionBuilder<TReadModel, TBuilder> NoAutoMap(); // returns the BASE interface
38
+
39
+ TBuilder WithInitialValues(Func<TReadModel> initialValueProviderCallback);
40
+ TBuilder From<TEvent>(Action<IFromBuilder<TReadModel, TEvent>>? builderCallback = default);
41
+ TBuilder Join<TEvent>(Action<IJoinBuilder<TReadModel, TEvent>>? builderCallback = default);
42
+ TBuilder FromEvery(Action<IFromEveryBuilder<TReadModel>> builderCallback); // callback required
43
+ TBuilder FromAll(Action<IFromAllBuilder<TReadModel>> builderCallback); // callback required
44
+ TBuilder RemovedWith<TEvent>(Action<RemovedWithBuilder<TReadModel, TEvent>>? builderCallback = default);
45
+ TBuilder RemovedWithJoin<TEvent>(Action<RemovedWithJoinBuilder<TReadModel, TEvent>>? builderCallback = default);
46
+ TBuilder Children<TChildModel>(
47
+ Expression<Func<TReadModel, IEnumerable<TChildModel>>> targetProperty,
48
+ Action<IChildrenBuilder<TReadModel, TChildModel>> builderCallback);
49
+ TBuilder Nested<TNestedModel>(
50
+ Expression<Func<TReadModel, TNestedModel?>> targetProperty,
51
+ Action<INestedBuilder<TReadModel, TNestedModel>> builderCallback);
52
+ ```
53
+
54
+ **`AutoMap()` and `NoAutoMap()` return the base interface, not `TBuilder`.**
55
+ Chaining `.NotRewindable()`, `.Passive()`, `.ContainerName()`, or
56
+ `.FromEventSequence()` after either of them does not compile. Put them last.
57
+
58
+ `RemovedWith` and `RemovedWithJoin` hand you the **concrete**
59
+ `RemovedWithBuilder<,>` and `RemovedWithJoinBuilder<,>` classes, not interfaces.
60
+
61
+ ## Keys live on the per-event builder
62
+
63
+ `From<TEvent>` hands the callback an `IFromBuilder<TReadModel, TEvent>`, which
64
+ derives from `IReadModelPropertiesBuilder<TReadModel, TEvent, TBuilder>`. That
65
+ is where every key method lives:
66
+
67
+ ```csharp
68
+ TBuilder UsingKey<TProperty>(Expression<Func<TEvent, TProperty>> keyAccessor);
69
+ TBuilder UsingKeyFromContext<TProperty>(Expression<Func<EventContext, TProperty>> keyAccessor);
70
+ TBuilder UsingParentKey<TProperty>(Expression<Func<TEvent, TProperty>> keyAccessor);
71
+ TBuilder UsingParentKeyFromContext<TProperty>(Expression<Func<EventContext, TProperty>> keyAccessor);
72
+ TBuilder UsingCompositeKey<TKeyType>(Action<ICompositeKeyBuilder<TKeyType, TEvent>> builderCallback);
73
+ TBuilder UsingParentCompositeKey<TKeyType>(Action<ICompositeKeyBuilder<TKeyType, TEvent>> builderCallback);
74
+ TBuilder UsingConstantKey(string value);
75
+ TBuilder UsingConstantParentKey(string value);
76
+ ```
77
+
78
+ The same interface carries the value operations:
79
+
80
+ ```csharp
81
+ TBuilder Increment<TProperty>(...);
82
+ TBuilder Decrement<TProperty>(...);
83
+ TBuilder Add<TProperty>(...);
84
+ TBuilder Subtract<TProperty>(...);
85
+ TBuilder Count<TProperty>(...);
86
+ TBuilder AddChild<TChildModel>(...);
87
+ ISetBuilder<...> Set(PropertyPath propertyPath);
88
+ ISetBuilder<...> Set<TProperty>(Expression<Func<TReadModel, TProperty>> propertyExpression);
89
+ TBuilder SetThisValue();
90
+ TBuilder Clear<TProperty>(...); // default implementation throws unless the builder overrides it
91
+ ```
92
+
93
+ ## `Set(...).To(...)`
94
+
95
+ ```csharp
96
+ // ISetBuilder<TReadModel, TEvent, TParentBuilder>
97
+ TParentBuilder To(PropertyPath propertyPath);
98
+ TParentBuilder ToEventSourceId();
99
+
100
+ // ISetBuilder<TReadModel, TEvent, TProperty, TParentBuilder>
101
+ TParentBuilder ToValue(TProperty value);
102
+ TParentBuilder To(Expression<Func<TEvent, TProperty>> eventPropertyAccessor);
103
+ TParentBuilder ToEventContextProperty(Expression<Func<EventContext, object>> eventContextPropertyAccessor);
104
+ ```
105
+
106
+ ## When the fluent form is the only option
107
+
108
+ The clearest case is a child collection fed by events that carry **different key
109
+ properties**, which the single-key model-bound `[ChildrenFrom<T>]` cannot state:
110
+
111
+ ```csharp
112
+ public void Define(IProjectionBuilderFor<<ReadModelName>> builder) =>
113
+ builder
114
+ .From<<AssignedEvent>>(from => from.UsingKey(@event => @event.<KeyA>))
115
+ .From<<UpdatedEvent>>(from => from.UsingKey(@event => @event.<KeyB>));
116
+ ```
117
+
118
+ Also reach for it for joins across several events, composite keys, initial
119
+ values, and conditional setters the attributes do not express.
120
+
121
+ ## AutoMap default
122
+
123
+ `ProjectionBuilderFor` is constructed with AutoMap enabled, and the model-bound
124
+ builder sets it to enabled unless `[NoAutoMap]` is present on the read model.
125
+ Children and nested builders are created with an inherit setting, so they follow
126
+ the enclosing scope.
@@ -0,0 +1,118 @@
1
+ # Model-bound projection attributes
2
+
3
+ Verified against `Cratis.Chronicle` `16.45.2`.
4
+
5
+ Unless noted, every attribute below lives in
6
+ `Cratis.Chronicle.Projections.ModelBound`. Generic arity is stated exactly:
7
+ `<T>` means one generic parameter naming the event type; "non-generic" means the
8
+ attribute takes no generic parameter at all.
9
+
10
+ ## Namespaces that are not `Projections.ModelBound`
11
+
12
+ | Attribute | Namespace |
13
+ | --- | --- |
14
+ | `[Key]` | `Cratis.Chronicle.Keys` |
15
+ | `[Index]` | `Cratis.Chronicle.ReadModels` |
16
+ | `[Passive]` | `Cratis.Chronicle.ReadModels` |
17
+ | `[NoAutoMap]` | `Cratis.Chronicle.Projections` |
18
+ | `[Projection]` | `Cratis.Chronicle.Projections` |
19
+ | `[EventSequence]`, `[EventLog]` | `Cratis.Chronicle.EventSequences` |
20
+ | `[EventStore]` | `Cratis.Chronicle.Events` |
21
+
22
+ `[ReadModel]` and `[Path]` are **Arc** types in `Cratis.Arc.Queries.ModelBound`.
23
+
24
+ ## Source and shape
25
+
26
+ | Attribute | Arity | Parameters | Targets |
27
+ | --- | --- | --- | --- |
28
+ | `[FromEvent<T>]` | `<T>` | `(string? key = default, string? parentKey = default)`, plus `ConstantKey { get; init; }` | class, struct; repeatable |
29
+ | `[RemovedWith<T>]` | `<T>` | `(string? key = default, string? parentKey = default)` | class, struct, property, parameter; repeatable |
30
+ | `[RemovedWithJoin<T>]` | `<T>` | `(string? key = default)` | property, parameter; repeatable |
31
+ | `[Nested]` | non-generic | none | property, parameter; single |
32
+ | `[FromAll]` | non-generic | `(string? contextProperty = default, string? property = default)` | **property only**; single |
33
+ | `[FromEvery]` | non-generic | `(string? property = default, string? contextProperty = default)` | property, parameter; single |
34
+ | `[NoAutoMap]` | non-generic | none | class, struct, property, parameter; inherited |
35
+ | `[NotRewindable]` | non-generic | none | class, struct; single |
36
+ | `[Passive]` | non-generic | none | class, struct; single |
37
+ | `[Key]` | non-generic | none | property, parameter |
38
+ | `[Index]` | non-generic | none | property, parameter |
39
+
40
+ Note that `[FromAll]` and `[FromEvery]` take their two optional arguments in
41
+ **opposite order**. Always pass them by name.
42
+
43
+ ## Setting values
44
+
45
+ | Attribute | Arity | Parameters | Targets |
46
+ | --- | --- | --- | --- |
47
+ | `[SetFrom<T>]` | `<T>` | `(string? eventPropertyName = default)` | property, parameter; repeatable |
48
+ | `[SetValue<T>]` | `<T>` | `(object? value)` — **required**; pass `null` to clear | property, parameter; repeatable |
49
+ | `[SetFromContext<T>]` | `<T>` | `(string? contextPropertyName = default)` | property, parameter; repeatable |
50
+ | `[AddFrom<T>]` | `<T>` | `(string? eventPropertyName = default)` | property, parameter; repeatable |
51
+ | `[SubtractFrom<T>]` | `<T>` | `(string? eventPropertyName = default)` | property, parameter; repeatable |
52
+ | `[ClearWith<T>]` | `<T>` | **none** | class, property, parameter; repeatable |
53
+
54
+ ## Counters
55
+
56
+ | Attribute | Arity | Parameters | Targets |
57
+ | --- | --- | --- | --- |
58
+ | `[Count<T>]` | `<T>` | none; `ConstantKey { get; init; }` | property, parameter; repeatable |
59
+ | `[Increment<T>]` | `<T>` | none; `ConstantKey { get; init; }` | property, parameter; repeatable |
60
+ | `[Decrement<T>]` | `<T>` | none; `ConstantKey { get; init; }` | property, parameter; repeatable |
61
+
62
+ `ConstantKey` is an **object-initializer property**, never a constructor
63
+ argument:
64
+
65
+ ```csharp
66
+ [Count<<EventName>>(ConstantKey = "<key>")]
67
+ ```
68
+
69
+ Setting it routes every matching event into a single aggregating document at
70
+ that key. That is a different mechanism from the fluent builder's
71
+ `UsingConstantKey("<key>")`, even though the effect is similar.
72
+
73
+ ## Relationships
74
+
75
+ | Attribute | Arity | Parameters | Targets |
76
+ | --- | --- | --- | --- |
77
+ | `[ChildrenFrom<T>]` | `<T>` | `(string? key = default, string? identifiedBy = default, string? parentKey = default)` — three parameters, `identifiedBy` in the middle | property, parameter; repeatable |
78
+ | `[Join<T>]` | `<T>` | `(string? on = default, string? eventPropertyName = default)` | property, parameter; repeatable |
79
+
80
+ `[Join<T>]` joins on an **event**. Joining on a read model is not supported.
81
+
82
+ ## Source selection
83
+
84
+ | Attribute | Parameters | Targets |
85
+ | --- | --- | --- |
86
+ | `[EventSequence("<name>")]` | `(string sequence)`; exposes `Sequence` | class |
87
+ | `[EventLog]` | none; equivalent to `[EventSequence]` for the event log | class |
88
+ | `[EventStore("<name>")]` | `(string eventStore)` | class, assembly; single |
89
+
90
+ **`[FromEventSequence]` was removed and does not exist.** The fluent builder's
91
+ `FromEventSequence(EventSequenceId)` method is unrelated and still present.
92
+
93
+ ## Labels, not filters
94
+
95
+ | Attribute | Namespace | Parameters | Effect on a projection |
96
+ | --- | --- | --- | --- |
97
+ | `[Tag]` | `Cratis.Chronicle` | `(params string[] tags)`; repeatable | label only |
98
+ | `[Tags]` | `Cratis.Chronicle` | `(params string[] tags)`; repeatable | label only |
99
+
100
+ A projection's definition carries `Tags` but has **no filter field at all**.
101
+ `[FilterEventsByTag]`, `[EventSourceType]`, and `[EventStreamType]` only filter
102
+ for reducers and reactors.
103
+
104
+ ## What makes a type a model-bound projection
105
+
106
+ Chronicle treats a type as a model-bound projection when the class, its primary
107
+ constructor parameters, or its public instance properties carry an
108
+ `[EventSequence]` attribute or any projection annotation — **except
109
+ `[Passive]`**, which is deliberately excluded. `[Passive]` on its own therefore
110
+ does not make a read model a projection; it changes how an existing projection
111
+ is registered.
112
+
113
+ ## AutoMap
114
+
115
+ AutoMap is enabled by default for both model-bound types and the fluent builder.
116
+ `[NoAutoMap]` disables it, is inherited, and applies at class, struct, property,
117
+ or parameter level. Child and nested builders inherit the enclosing setting.
118
+ Call `.AutoMap()` only to re-enable inside a scope you disabled.
@@ -0,0 +1,2 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the project root for full license information.