@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,198 @@
1
+ ---
2
+ name: cratis-fundamentals-concept
3
+ description: Create strongly typed Cratis domain values with ConceptAs<T> and Chronicle event-source identities with EventSourceId<T>. Use when a C# domain value has meaning beyond its primitive or when an identity is actually used as a Chronicle event-source/stream ID. Do not use for enums, DTO-only transport values, arbitrary non-stream entity IDs, or event schema migration.
4
+ license: MIT
5
+ ---
6
+
7
+ # Cratis domain concepts and event-source identities
8
+
9
+ Replace a primitive only when the domain gives it distinct meaning. Keep value
10
+ concepts and Chronicle stream identities separate.
11
+
12
+ ## Verified product sources
13
+
14
+ This skill is verified against these exact public releases:
15
+
16
+ | Package | Version | Purpose |
17
+ | --- | --- | --- |
18
+ | `Cratis.Fundamentals` | `7.18.1` | `Cratis.Concepts.ConceptAs<T>` |
19
+ | `Cratis.Chronicle` | `16.38.1` | `Cratis.Chronicle.Events.EventSourceId` and `EventSourceId<T>` |
20
+
21
+ Reverify product sources before claiming support for another version.
22
+
23
+ ## Choose the type
24
+
25
+ - Derive a name, amount, code, number, or non-stream entity ID from
26
+ `ConceptAs<T>`.
27
+ - Derive an identity from `EventSourceId<T>` only when that value is actually
28
+ passed to Chronicle as the event-source/stream ID.
29
+ - Do not use `ConceptAs<Guid>` for a Chronicle stream identity.
30
+ - Do not use `EventSourceId<T>` merely because a value is called an ID.
31
+ - Do not wrap an enum. An enum already expresses a closed domain concept.
32
+ - Keep DTO-only transport values primitive unless the domain type belongs in the
33
+ public contract.
34
+
35
+ Both generic bases require an underlying type that implements `IComparable`.
36
+
37
+ ## Create a value concept
38
+
39
+ A value concept contains exactly one wrapped value. Do not add extra properties;
40
+ Fundamentals converters assume the concept is a single-value type and additional
41
+ state can be lost during serialization.
42
+
43
+ ```csharp
44
+ // Copyright (c) Cratis. All rights reserved.
45
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
46
+
47
+ using Cratis.Concepts;
48
+
49
+ namespace <NamespaceRoot>.<Feature>;
50
+
51
+ /// <summary>
52
+ /// Represents the <description>.
53
+ /// </summary>
54
+ /// <param name="Value">The underlying value.</param>
55
+ public record <ConceptName>(<ComparableUnderlyingType> Value) :
56
+ ConceptAs<<ComparableUnderlyingType>>(Value);
57
+ ```
58
+
59
+ `ConceptAs<T>` supplies implicit conversion from the concept to `T`. Add the
60
+ reverse conversion only when it improves the domain API:
61
+
62
+ ```csharp
63
+ public static implicit operator <ConceptName>(<ComparableUnderlyingType> value) =>
64
+ new(value);
65
+ ```
66
+
67
+ Primitive-to-concept conversion is optional; it is not a Fundamentals
68
+ requirement.
69
+
70
+ ### Absence and sentinels
71
+
72
+ `ConceptAs<T>` rejects a null wrapped value. Represent absence with a nullable
73
+ concept reference such as `<ConceptName>?` when absence is valid.
74
+
75
+ A `NotSet` or `Empty` value is optional domain policy. Add one only when the
76
+ chosen primitive value is impossible or explicitly reserved in that domain.
77
+ Do not assume `string.Empty`, `0`, or `Guid.Empty` is universally invalid.
78
+
79
+ ## Create a Guid-backed Chronicle stream identity
80
+
81
+ Use this shape only for an identity actually supplied to Chronicle append/read
82
+ operations as the event-source ID.
83
+
84
+ ```csharp
85
+ // Copyright (c) Cratis. All rights reserved.
86
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
87
+
88
+ using Cratis.Chronicle.Events;
89
+
90
+ namespace <NamespaceRoot>.<Feature>;
91
+
92
+ /// <summary>
93
+ /// Represents the event-source identity of a <description>.
94
+ /// </summary>
95
+ /// <param name="Value">The underlying Guid value.</param>
96
+ public record <ConceptName>(Guid Value) : EventSourceId<Guid>(Value)
97
+ {
98
+ /// <summary>
99
+ /// Creates a new <ConceptName>.
100
+ /// </summary>
101
+ /// <returns>A new <ConceptName>.</returns>
102
+ public static <ConceptName> New() => new(Guid.NewGuid());
103
+
104
+ /// <summary>
105
+ /// Converts a Guid to a <ConceptName>.
106
+ /// </summary>
107
+ public static implicit operator <ConceptName>(Guid value) => new(value);
108
+ }
109
+ ```
110
+
111
+ `New()` and the primitive-to-derived conversion are conveniences on this domain
112
+ type. `EventSourceId<T>` does not construct an arbitrary derived identity for
113
+ you.
114
+
115
+ ## Create a non-Guid Chronicle stream identity
116
+
117
+ Use a factory only when the domain has an authoritative way to create the
118
+ underlying value.
119
+
120
+ ```csharp
121
+ // Copyright (c) Cratis. All rights reserved.
122
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
123
+
124
+ using Cratis.Chronicle.Events;
125
+
126
+ namespace <NamespaceRoot>.<Feature>;
127
+
128
+ /// <summary>
129
+ /// Represents the event-source identity of a <description>.
130
+ /// </summary>
131
+ /// <param name="Value">The underlying value.</param>
132
+ public record <ConceptName>(<ComparableUnderlyingType> Value) :
133
+ EventSourceId<<ComparableUnderlyingType>>(Value)
134
+ {
135
+ /// <summary>
136
+ /// Converts the underlying value to a <ConceptName>.
137
+ /// </summary>
138
+ public static implicit operator <ConceptName>(<ComparableUnderlyingType> value) =>
139
+ new(value);
140
+ }
141
+ ```
142
+
143
+ The exact `EventSourceId<T>` base supports conversions among `T`, string,
144
+ untyped `EventSourceId`, and `EventSourceId<T>`. Those operators do not create
145
+ your derived `<ConceptName>` from `T`, string, or untyped `EventSourceId`.
146
+ Declare only the derived-type conversions your domain API needs.
147
+
148
+ String and Guid are the safest round-trip primitives. Chronicle also supports
149
+ constructible `ConceptAs<string>` and `ConceptAs<Guid>` values. Other comparable
150
+ values rely on `Convert.ChangeType`; verify round-trip behavior before using
151
+ them as stream IDs.
152
+
153
+ ### Unspecified and sensitive identities
154
+
155
+ `EventSourceId.Unspecified` belongs to the untyped string-backed ID.
156
+ `Guid.Empty`, `0`, `0L`, and similar typed values become real, specified stream
157
+ IDs after conversion; they are not Chronicle's unspecified value. Treat any
158
+ sentinel on a typed identity as explicit domain policy, not framework behavior.
159
+
160
+ Never use a sensitive natural identifier directly as an event-source ID.
161
+ Chronicle cannot encrypt event-source IDs. Use a random surrogate stream ID and
162
+ store the sensitive value separately under the approved compliance model.
163
+
164
+ ## Use the identity with Chronicle
165
+
166
+ Pass the typed identity as the append/read event-source ID. Merely declaring an
167
+ `EventSourceId<T>` property does not select the event stream.
168
+
169
+ Do not add `[Key]` or `[Subject]` to an `EventSourceId<T>`-derived member;
170
+ Chronicle analyzer `CHR0026` reports that misuse. Do not add `[PII]` to an
171
+ event-source ID; analyzer `CHR0034` rejects it.
172
+
173
+ ## Placement is an application convention
174
+
175
+ In a Cratis application, place the concept with the feature that owns its
176
+ meaning rather than in a generic `Concepts/` folder. Put genuinely cross-feature
177
+ concepts in `Common/`. Do not introduce a top-level `Features/` wrapper.
178
+
179
+ This placement is a Cratis application convention, not a Fundamentals or
180
+ Chronicle API requirement. Framework and client repositories follow their own
181
+ repository structure.
182
+
183
+ ## Verify
184
+
185
+ - `ConceptAs<T>` and `EventSourceId<T>` use an `IComparable` underlying type.
186
+ - A concept contains exactly one wrapped value and no extra properties.
187
+ - Enums remain enums.
188
+ - Null absence uses a nullable concept reference rather than a null wrapped
189
+ value.
190
+ - Primitive-to-derived conversions and sentinels exist only when justified by
191
+ the domain.
192
+ - An `EventSourceId<T>` type represents a real Chronicle stream identity.
193
+ - The typed identity is passed explicitly to Chronicle operations.
194
+ - No `[Key]`, `[Subject]`, or `[PII]` attribute is placed on the stream identity.
195
+ - Sensitive natural identifiers use a surrogate stream ID.
196
+ - The file carries the repository license header.
197
+ - The project builds and its relevant specifications pass against the verified
198
+ package versions.
@@ -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.
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: cratis-fundamentals-type-discovery
3
+ description: Wire a type that must enumerate every implementation of an abstraction by injecting IInstancesOf<T> from Cratis.Types and deleting the hand-maintained service registrations. Use when a handler set, filter chain, strategy set, or dispatcher fans out to all implementations. Do not use for ordinary single-service dependency injection.
4
+ license: MIT
5
+ ---
6
+
7
+ # Cratis implementation discovery
8
+
9
+ `services.AddSingleton<IFoo, Foo>()` looks free at the registration line. The
10
+ cost arrives later: a new implementation in another folder does nothing until
11
+ someone remembers the line, a removed one leaves a registration that fails at
12
+ startup, and every specification setup has to mirror the composition root.
13
+
14
+ `IInstancesOf<T>` moves that knowledge into the implementation. Adding or
15
+ removing one becomes a single-file change.
16
+
17
+ ## Verified product sources
18
+
19
+ | Package | Version | Purpose |
20
+ | --- | --- | --- |
21
+ | `Cratis.Fundamentals` | `7.18.2` | `IInstancesOf<T>`, `IImplementationsOf<T>`, `[Singleton]`, `[Scoped]`, `[IgnoreConvention]`, the convention bindings |
22
+
23
+ Reverify against the Cratis Fundamentals repository before claiming support for
24
+ another version.
25
+
26
+ ## Route near misses
27
+
28
+ - The consumer needs **one** implementation chosen at composition time:
29
+ constructor-inject the interface and let the `IFoo → Foo` convention resolve
30
+ it. This skill does not apply.
31
+ - The method **returns** a sequence of values to a caller: `IEnumerable<T>` (or
32
+ `IReadOnlyList<T>`) is still the right return type. The rule is about
33
+ *injecting* implementations of an abstraction, not about returning values.
34
+ - You only need the **types**, never instances: inject `IImplementationsOf<T>`,
35
+ which enumerates `Type` rather than `T`.
36
+
37
+ ## When you need this
38
+
39
+ The consumer of an abstraction has to iterate, filter, or fan out to **every**
40
+ registered implementation:
41
+
42
+ - Anything plural that delegates to a set — `*Handlers`, `*Filters`,
43
+ `*Validators`, `*Formatters`, `*Strategies`, `*Providers`, `*Resolvers`.
44
+ - A dispatcher that asks each implementation `CanHandle(...)` and forwards to
45
+ the one that says yes.
46
+ - A composite that fans one input out to all implementations and aggregates.
47
+
48
+ ## Step 1 — Confirm the implementations are discoverable
49
+
50
+ `IInstancesOf<T>` finds types by convention across the loaded assemblies. The
51
+ requirements are only:
52
+
53
+ - Each implementation is a non-abstract `public class`.
54
+ - It implements the interface directly, not through a layer that hides it.
55
+ - `T` is an interface or abstract class — the interface is declared
56
+ `where T : class`.
57
+
58
+ No assembly attribute and no registration call is needed.
59
+
60
+ ## Step 2 — Choose the lifetime with an attribute
61
+
62
+ ```csharp
63
+ using Cratis.DependencyInjection;
64
+
65
+ [Singleton]
66
+ public class CsvReportFormatter(IClock clock) : IReportFormatter
67
+ {
68
+ public bool CanHandle(ReportRequest request) => request.Format == "csv";
69
+ public string Format(Report report) => /* … */;
70
+ }
71
+ ```
72
+
73
+ ⚠️ `[Singleton]` lives in **`Cratis.DependencyInjection`**, not `Cratis`. That
74
+ is the using directive to add when it does not resolve.
75
+
76
+ The convention reads exactly three attributes:
77
+
78
+ | Attribute | Lifetime |
79
+ | --- | --- |
80
+ | `[Singleton]` | Singleton |
81
+ | `[Scoped]` | Scoped |
82
+ | neither | **Transient** |
83
+
84
+ `[IgnoreConvention]` opts a type out of convention binding entirely.
85
+
86
+ Skip `[Singleton]` only when the implementation genuinely holds per-call state.
87
+ The `IFoo → Foo` convention still applies to transients — do not register them
88
+ explicitly either.
89
+
90
+ ## Step 3 — Inject `IInstancesOf<T>` in the consumer
91
+
92
+ ```csharp
93
+ using Cratis.DependencyInjection;
94
+ using Cratis.Types;
95
+
96
+ [Singleton]
97
+ public class ReportFormatters(IInstancesOf<IReportFormatter> formatters) : IReportFormatters
98
+ {
99
+ public bool CanHandle(ReportRequest request) =>
100
+ formatters.Any(formatter => formatter.CanHandle(request));
101
+
102
+ public string Format(ReportRequest request, Report report) =>
103
+ formatters.First(formatter => formatter.CanHandle(request)).Format(report);
104
+ }
105
+ ```
106
+
107
+ `IInstancesOf<T>` implements `IEnumerable<T>`, so LINQ works on it directly.
108
+
109
+ **It resolves an instance from the service provider on every enumeration**, not
110
+ once at construction. That is what makes a scoped or transient implementation
111
+ behave correctly, and it is why materializing with `.ToArray()` is worth doing
112
+ only when you genuinely need a stable snapshot within one operation.
113
+
114
+ ## Step 4 — Delete the dead registrations
115
+
116
+ Find every line in a composition root or service-collection extension that
117
+ registered the implementations or the consumer, and remove it:
118
+
119
+ ```csharp
120
+ // Delete — IInstancesOf<T> discovers them, [Singleton] gives them their lifetime
121
+ services.AddSingleton<IReportFormatter, CsvReportFormatter>();
122
+ services.AddSingleton<IReportFormatter, JsonReportFormatter>();
123
+ services.AddSingleton<IReportFormatters, ReportFormatters>();
124
+ ```
125
+
126
+ Search for every remaining reference before deleting, so nothing else was
127
+ relying on a registration for a different reason.
128
+
129
+ ## What breaks
130
+
131
+ - **`[Singleton]` does not resolve.** The using directive is
132
+ `Cratis.DependencyInjection`, not `Cratis`.
133
+ - **An implementation is silently absent from the set.** Its assembly is not
134
+ loaded at the point of enumeration, it is not `public`, it is abstract, or it
135
+ carries `[IgnoreConvention]`.
136
+ - **An implementation is a new instance every time it is touched.** It has no
137
+ lifetime attribute, so the convention made it transient. That is correct
138
+ behavior for the convention and usually the wrong intent — add `[Singleton]`.
139
+ - **A stale registration fails at startup.** A hand-written
140
+ `AddSingleton<TInterface, Impl>()` survived a type being removed. Step 4 is
141
+ what prevents it.
142
+ - **Injecting `IEnumerable<T>` seems to work and returns nothing.** That
143
+ signature only sees hand-registered implementations. It is the failure mode
144
+ this skill exists to remove — it fails silently and empty, not loudly.
145
+
146
+ ## How it is proven
147
+
148
+ `dotnet build` — zero warnings, zero errors — then run the specifications for
149
+ the affected behavior. The non-vacuity check matters here: assert the **count**
150
+ of discovered implementations, not just that iterating them threw nothing. A
151
+ fan-out over an empty set passes every assertion that only says "nothing went
152
+ wrong".
@@ -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.
@@ -0,0 +1,297 @@
1
+ ---
2
+ name: cratis-governed-release-methodology
3
+ description: Choose the assurance tier a release needs, gather the evidence that tier requires, label release intent by semantic-version impact, and structure a canary-before-promotion path with an explicit recovery disposition. Use when planning a release or judging whether checksums, provenance, an SBOM, or a claim of support are warranted. Do not use for product code, publish-command syntax, or release credentials.
4
+ license: MIT
5
+ ---
6
+
7
+ # Governed release methodology
8
+
9
+ Release engineering is the discipline of matching **ceremony to consequence**.
10
+ Too little, and a consumer upgrades into a broken build with no way back. Too
11
+ much, and every documentation fix drags a supply-chain ritual behind it until
12
+ the ritual is performed without belief and stops meaning anything.
13
+
14
+ This skill is product-, language-, and registry-independent. It applies to a
15
+ Cratis repository publishing NuGet packages, one publishing npm packages, one
16
+ pushing container images, and one whose whole delivery is a merge to `main`. It
17
+ grants no release authority of its own: it tells you what a claim costs, not
18
+ that you may make it.
19
+
20
+ ## Start with the artifact, not the process
21
+
22
+ The single most expensive mistake is applying package-release ceremony to
23
+ something that is not a package. Classify the artifact first.
24
+
25
+ | Artifact | What the consumer receives | Ceremony it earns |
26
+ | --- | --- | --- |
27
+ | Compiled package (NuGet, npm, Maven, Hex), container image, installer | Opaque bytes built on infrastructure the consumer cannot inspect | Full supply-chain receipts |
28
+ | Generated or bundled tree assembled by a pipeline | Bytes that do not exist in any reviewed source tree | Full supply-chain receipts |
29
+ | Source files a host reads directly from a git ref (rules, skills, templates) | Exactly the reviewed content at a commit | Review and versioning only |
30
+
31
+ The dividing question is **"can the consumer verify what they got against what
32
+ was reviewed, without trusting our build?"** If the host clones a git ref and
33
+ reads the files, git's own content addressing already answers that: the tree
34
+ hash *is* the checksum, the commit *is* the provenance, and the reviewed diff
35
+ *is* the artifact. Generating a checksum file for that content, publishing an
36
+ SBOM of markdown, and demanding a signed attestation before a folder may be read
37
+ adds process without adding a guarantee. Say so plainly rather than performing
38
+ it.
39
+
40
+ If the artifact is compiled or assembled, the opposite holds, and the
41
+ supply-chain receipts below are not optional decoration.
42
+
43
+ ## Pick the assurance tier before you pick the checks
44
+
45
+ Three tiers, each earning strictly more claim than the one below it. Default to
46
+ the lowest tier that supports the claim you intend to make, and never let a
47
+ lower tier's evidence be reinterpreted as a higher tier's.
48
+
49
+ **Candidate review** — an internal, non-published build for review. Claim:
50
+ "this builds deterministically from an immutable source and contains nothing it
51
+ should not." Requires deterministic regeneration from an exact revision, static
52
+ contract and schema validation, secret and path scanning, and an inventory
53
+ digest. It may never be installed by a consumer or described as available.
54
+
55
+ **Preview** — a real, published, low-commitment version (a `0.x.y`, a
56
+ prerelease channel). Claim: "a consumer can install this, use it, and get back
57
+ out." Requires everything above, plus an independent human review and a real
58
+ **exact-artifact** lifecycle smoke: pack, install, discover, use, uninstall, and
59
+ roll back to a prior exact version. A preview may be withdrawn, may break, and
60
+ must never be described as supported.
61
+
62
+ **Governed support** — the claim that a consumer may depend on this. Requires
63
+ everything above, plus the full lifecycle phase set on a real host, external
64
+ control attestations, a canary against a real consumer, a recorded recovery
65
+ disposition, and a named human approval for that exact released artifact.
66
+
67
+ Graduation between tiers does not change the artifact's format. It changes
68
+ what has been observed about it. A preview that later graduates re-runs the
69
+ evidence for the exact released version; earlier observations of an earlier
70
+ build do not transfer.
71
+
72
+ ## Evidence is an observation, not an intention
73
+
74
+ "Evidence" degrades into paperwork the moment it is allowed to mean "we
75
+ implemented the thing that would produce it." Hold the line with a technical
76
+ ladder where each rung names an observation someone actually made:
77
+
78
+ 1. **documented** — the behavior is written down.
79
+ 2. **generated** — an artifact is produced deterministically.
80
+ 3. **statically validated** — schemas, contracts, and scans pass on it.
81
+ 4. **install-tested** — it installs into a real host.
82
+ 5. **behavior-tested** — it is discovered, does the right thing on a positive
83
+ case, and correctly refuses a negative case.
84
+ 6. **lifecycle-tested** — install, update, rollback, uninstall, and preservation
85
+ of consumer-owned state all pass on that exact artifact.
86
+ 7. **release-tested** — the *released* artifact (not a local build of the same
87
+ commit) passed a canary.
88
+ 8. **supported** — a named human approved that exact released artifact.
89
+
90
+ Two rules keep the ladder honest:
91
+
92
+ - **Synthetic evidence caps out at statically validated.** A fixture, simulator,
93
+ or mocked host proves the pipeline, never the host. Classify every observation
94
+ as synthetic, local, hosted, or real-consumer, and never let a synthetic
95
+ observation satisfy an execution requirement.
96
+ - **A missing or version-mismatched host is a blocked outcome, not a skip.** If
97
+ the canary needed host 2.1.245 and the runner had 2.1.235, the phase did not
98
+ pass; it did not run. A skipped phase that reports green is worse than a red
99
+ one, because it retires the question.
100
+
101
+ ## The lifecycle phases a real install cycle must cover
102
+
103
+ When you claim a consumer can adopt and un-adopt your artifact, these are the
104
+ phases that claim decomposes into. Run them against the exact artifact a
105
+ consumer would receive.
106
+
107
+ - **collision-negative** — with the artifact absent, nothing already present
108
+ answers to its name. This is the baseline that makes the positive result mean
109
+ something.
110
+ - **install** — it installs from the published location, not a local path.
111
+ - **discovery** — the host actually finds and lists it afterwards.
112
+ - **behavior-positive** — it does the thing it exists to do.
113
+ - **behavior-negative** — it declines the case it must decline. An artifact that
114
+ never says no has not been tested for judgment.
115
+ - **update** — moving from the previous version to this one succeeds.
116
+ - **rollback** — moving back to the previous exact version succeeds. This is the
117
+ phase teams skip and the one consumers need most.
118
+ - **uninstall** — it removes cleanly.
119
+ - **project-context-preservation** — consumer-owned files it never owned are
120
+ untouched by install, update, rollback, and uninstall. Destroying local
121
+ configuration during an upgrade is the most expensive failure in this list.
122
+ - **cleanup** — no residue in caches, registries, or host state.
123
+
124
+ Rollback and preservation are what convert "it works" into "it is safe to try."
125
+ A release path without a proven way back is not a release path; it is a
126
+ one-way door.
127
+
128
+ ## Supply-chain receipts and what each one proves
129
+
130
+ For compiled or assembled artifacts only, and each for a distinct reason:
131
+
132
+ - **Checksums** prove the bytes a consumer downloaded are the bytes that were
133
+ built. They defend against a corrupted or substituted download.
134
+ - **Provenance** proves *which* build, from which source revision, on which
135
+ workflow, produced those bytes. It defends against a package that matches its
136
+ own checksum but was built from something nobody reviewed.
137
+ - **SBOM** proves what is *inside* the artifact. It is what makes a downstream
138
+ vulnerability answerable at all: without it, "are we affected?" requires
139
+ re-deriving the dependency closure of a shipped binary.
140
+ - **Canary receipt** proves the released artifact worked for a real consumer.
141
+ - **Promotion receipt** proves the decision to widen the audience came after the
142
+ canary, and records what it was based on.
143
+ - **Recovery disposition** records, before publication, exactly what can be
144
+ undone and what cannot.
145
+ - **Support approval** is a named human accepting responsibility for that exact
146
+ released artifact.
147
+
148
+ Each receipt is written **at the stage it describes and never backdated**. A
149
+ publication receipt cannot be a prepublication prerequisite; a promotion receipt
150
+ cannot be written before the canary it cites. If an earlier record has to be
151
+ rewritten to make a later claim true, the later claim is false.
152
+
153
+ Prefer registry-native provenance (trusted publishing / OIDC) over
154
+ hand-assembled attestations. It removes the long-lived credential, and the
155
+ consumer can verify it without trusting a file you wrote.
156
+
157
+ ## Automation is not a control
158
+
159
+ The most common false claim in release engineering is that implemented
160
+ automation proves a control is configured. It does not. A workflow that *would*
161
+ publish through a protected environment says nothing about whether the
162
+ environment exists, who can approve it, or who owns the package name.
163
+
164
+ Before claiming a control, record an attestation binding: the exact subject
165
+ (repository, branch, workflow, environment, package, or publisher account), who
166
+ observed it and with what authority, when, how long the observation is good
167
+ for, where revocation would be visible, and when revocation was last checked.
168
+ An attestation without an expiry and a revocation check is a screenshot.
169
+
170
+ Controls worth attesting for a real publishing pipeline: protected default
171
+ branch with required status checks, a release-specific required status, a
172
+ protected publication environment, repository-scoped credentials, exact package
173
+ name ownership in the registry, and exact trusted-publisher registration.
174
+
175
+ ## Label release intent by outward-facing effect
176
+
177
+ The version label on a pull request is not paperwork describing the change; in
178
+ a label-driven pipeline it **is the decision to ship**. Exactly one of four
179
+ labels belongs on every pull request:
180
+
181
+ - **major** — a breaking change to public API or observable behavior.
182
+ - **minor** — new capability, backward compatible.
183
+ - **patch** — a fix, or a refactor with identical observable behavior.
184
+ - **no-release** — nothing a consumer could observe by upgrading.
185
+
186
+ The test is **outward-facing effect, not file location**. A change under a
187
+ source directory that only touches tests is not shippable; a one-line change to
188
+ a shipped package's behavior is, however small. Documentation, CI workflows,
189
+ test-only changes, and local tooling carry `no-release`.
190
+
191
+ `no-release` is a decision, not an omission — leaving the label off is
192
+ indistinguishable from forgetting it, so an unlabeled pull request stays an
193
+ error. Never default to `patch` when unsure: an unnecessary release burns a
194
+ version number, ships release notes describing nothing, and buries the releases
195
+ that matter.
196
+
197
+ Two consequences worth internalizing:
198
+
199
+ - **Group related small work into one pull request.** Several small merges
200
+ become several releases, and a stream of near-empty patch releases makes the
201
+ release history useless to the people it is written for. The pull request is
202
+ the release boundary; make it a coherent, describable change.
203
+ - **Folding one pull request into another means relabeling it.** When branch X
204
+ is merged as part of Y, X auto-closes *as merged* and fires its own publish
205
+ run under its own label. Relabel X to `no-release`; do not close it by hand,
206
+ or its author loses the attribution.
207
+
208
+ ## Structure the release path so no step can vouch for itself
209
+
210
+ The ordering below is what makes the evidence non-circular. Each step consumes
211
+ only records that already existed when it started.
212
+
213
+ 1. **Prerequisites first.** Every approval, evidence record, and control
214
+ attestation the release will cite already exists on the protected base
215
+ branch. A release request may not introduce, weaken, or grant its own
216
+ prerequisites.
217
+ 2. **Freeze a preflight snapshot.** Bind the exact source revision, the exact
218
+ candidate artifact digest, and the exact prerequisite set — without including
219
+ the request that will cite it.
220
+ 3. **One append-only request.** One version, one artifact, referencing the
221
+ preflight digest. Requests are never rewritten or reused; a defect in a
222
+ released version is corrected by a new version, never by editing the record
223
+ of the old one.
224
+ 4. **Named review, then merge.** Merging is the human approval. Keep the merge
225
+ a true merge commit so the branch's real history survives, and bind the
226
+ approval to the parent commit so unrelated authority changes cannot be
227
+ batched in ahead of it.
228
+ 5. **Publish, then write the publication receipt** with package identity,
229
+ artifact digest, provenance, SBOM, and checksums.
230
+ 6. **Canary the released artifact against a real consumer** — a real downstream
231
+ repository or sample that actually depends on it, running its own build and
232
+ tests against the published version. A canary against a fixture proves the
233
+ pipeline; a canary against a real consumer proves the release. Publication
234
+ jobs must stop before promotion if it fails.
235
+ 7. **Promote only on canary evidence**, and record the promotion separately.
236
+ 8. **Support requires its own final named approval** for that exact released
237
+ artifact. Publication is not promotion, promotion is not support, and a
238
+ marketplace or registry listing is none of the three.
239
+
240
+ ## Write the recovery disposition before you publish
241
+
242
+ Decide and record, per stage, what failure means — while you still have the
243
+ choice.
244
+
245
+ - **Before publication**, everything is reversible: delete the draft release,
246
+ close the index or subscriber pull request, remove the branch and tag.
247
+ - **After publication, most registries are immutable.** A published npm, NuGet,
248
+ or Maven version cannot be un-published in any way a consumer can rely on.
249
+ The honest recovery is roll-*forward*: publish a corrected version and move
250
+ the moving pointer (`latest`, a floating tag) back to a known-good exact
251
+ version. Never describe an immutable version as rolled back.
252
+ - **Automatic rollback is a capability, not an assumption.** Claim it only where
253
+ it is implemented and canaried; otherwise record it as disabled and name the
254
+ manual procedure.
255
+ - **Keep failed intermediate state for inspection** rather than deleting the
256
+ evidence of the failure.
257
+
258
+ State the disposition in terms a consumer can act on: which exact version to
259
+ pin to, and what the pinned version does not have.
260
+
261
+ ## Never claim a tier you have not paid for
262
+
263
+ The final discipline is linguistic. "Available", "published", "preview",
264
+ "listed", and "supported" are different claims with different costs, and the
265
+ gap between them is where trust is lost.
266
+
267
+ - Publishing a package makes it **available**, not supported.
268
+ - A marketplace submission is not a listing; a listing is not support.
269
+ - Passing static validation makes an artifact **statically validated**, not
270
+ install-tested.
271
+ - Automation that could produce a receipt has not produced one.
272
+
273
+ When the evidence for a claim is missing, say which evidence is missing and what
274
+ the artifact *is* — not a hedged version of the claim you wanted to make.
275
+
276
+ ## Verify
277
+
278
+ - The artifact is classified as compiled/assembled or as source read from a git
279
+ ref, and the ceremony matches that classification.
280
+ - The chosen tier is the lowest one that supports the intended claim.
281
+ - Every phase result is pass, fail, or explicitly blocked — never a silent skip.
282
+ - No synthetic or fixture observation is counted as install, behavior,
283
+ lifecycle, or canary evidence.
284
+ - Rollback to a prior exact version and preservation of consumer-owned files
285
+ were both actually exercised.
286
+ - Checksums, provenance, and SBOM exist for compiled or assembled artifacts and
287
+ are absent, deliberately and explainably, for git-ref source delivery.
288
+ - Every claimed external control has an attestation with subject, issuer,
289
+ validity, and a revocation check.
290
+ - The pull request carries exactly one of `major`, `minor`, `patch`, or
291
+ `no-release`, chosen by outward-facing effect.
292
+ - No record was rewritten or backdated to satisfy a later stage.
293
+ - A canary ran against a real consumer before promotion.
294
+ - The recovery disposition is written down and does not claim an immutable
295
+ version can be rolled back.
296
+ - Nothing is described as supported without a named approval of that exact
297
+ released artifact.
@@ -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.