@cratis/pi 0.0.1 → 2.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.
- package/LICENSE +21 -0
- package/README.md +18 -37
- package/package/corpus/agents/backend-developer.md +125 -0
- package/package/corpus/agents/code-reviewer.md +165 -0
- package/package/corpus/agents/coordinator.md +163 -0
- package/package/corpus/agents/frontend-developer.md +246 -0
- package/package/corpus/agents/orchestrator.md +196 -0
- package/package/corpus/agents/performance-reviewer.md +109 -0
- package/package/corpus/agents/planner.md +145 -0
- package/package/corpus/agents/repository-investigation-reviewer.md +45 -0
- package/package/corpus/agents/repository-investigator.md +50 -0
- package/package/corpus/agents/security-reviewer.md +118 -0
- package/package/corpus/agents/slice-implementer.md +59 -0
- package/package/corpus/agents/spec-writer.md +149 -0
- package/package/corpus/harnesses/pi/extensions/cratis-hooks/index.ts +213 -0
- package/package/corpus/harnesses/pi/extensions/cratis-rules/index.ts +27 -0
- package/package/corpus/harnesses/pi/extensions/package.json +4 -0
- package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +167 -0
- package/package/corpus/harnesses/pi/extensions/subagent/index.ts +352 -0
- package/package/corpus/hooks/README.md +434 -0
- package/package/corpus/hooks/agent-stop.md +49 -0
- package/package/corpus/hooks/pre-commit.md +47 -0
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +87 -0
- package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +11 -0
- package/package/corpus/hooks/scripts/cratis-pattern-scan.sh +197 -0
- package/package/corpus/hooks/scripts/cratis-patterns.json +84 -0
- package/package/corpus/hooks/scripts/cratis-quality-gate.sh +219 -0
- package/package/corpus/hooks/scripts/hook-lib.sh +152 -0
- package/package/corpus/hooks/scripts/quality-gates.json +261 -0
- package/package/corpus/hooks/scripts/type-references-allowlist.txt +71 -0
- package/package/corpus/hooks/scripts/validate-package-imports.sh +166 -0
- package/package/corpus/hooks/scripts/validate-package-subpaths.sh +120 -0
- package/package/corpus/hooks/scripts/validate-type-references.sh +308 -0
- package/package/corpus/hooks/settings.template.json +40 -0
- package/package/corpus/prompts/add-business-rule.prompt.md +22 -0
- package/package/corpus/prompts/add-concept.prompt.md +17 -0
- package/package/corpus/prompts/add-ef-migration.prompt.md +24 -0
- package/package/corpus/prompts/add-projection.prompt.md +20 -0
- package/package/corpus/prompts/add-reactor.prompt.md +22 -0
- package/package/corpus/prompts/add-reducer.prompt.md +20 -0
- package/package/corpus/prompts/audit-hooks.prompt.md +15 -0
- package/package/corpus/prompts/check-doc-drift.prompt.md +21 -0
- package/package/corpus/prompts/code-review.prompt.md +9 -0
- package/package/corpus/prompts/new-feature.prompt.md +9 -0
- package/package/corpus/prompts/new-vertical-slice.prompt.md +18 -0
- package/package/corpus/prompts/review-pr.prompt.md +35 -0
- package/package/corpus/prompts/review-skill.prompt.md +16 -0
- package/package/corpus/prompts/scaffold-feature.prompt.md +16 -0
- package/package/corpus/prompts/ship-changes.prompt.md +20 -0
- package/package/corpus/prompts/verify-ai-setup.prompt.md +19 -0
- package/package/corpus/prompts/write-documentation.prompt.md +21 -0
- package/package/corpus/prompts/write-specs.prompt.md +22 -0
- package/package/corpus/rules/capability-is-not-authority.md +31 -0
- package/package/corpus/rules/code-quality.csharp.md +91 -0
- package/package/corpus/rules/code-quality.md +82 -0
- package/package/corpus/rules/code-quality.typescript.md +89 -0
- package/package/corpus/rules/components.md +207 -0
- package/package/corpus/rules/concepts.md +115 -0
- package/package/corpus/rules/csharp.md +269 -0
- package/package/corpus/rules/dialogs.md +264 -0
- package/package/corpus/rules/documentation-structure-and-formatting.md +148 -0
- package/package/corpus/rules/documentation.md +90 -0
- package/package/corpus/rules/editing-cratis-docs.md +69 -0
- package/package/corpus/rules/efcore.md +235 -0
- package/package/corpus/rules/efcore.specs.md +44 -0
- package/package/corpus/rules/exit-codes-and-wrappers.md +33 -0
- package/package/corpus/rules/framework.md +52 -0
- package/package/corpus/rules/frontend-quality.md +59 -0
- package/package/corpus/rules/frontend-testing.md +126 -0
- package/package/corpus/rules/general.md +305 -0
- package/package/corpus/rules/git-commits.md +138 -0
- package/package/corpus/rules/github-actions.md +92 -0
- package/package/corpus/rules/glossary.md +61 -0
- package/package/corpus/rules/guards-and-fuses.md +45 -0
- package/package/corpus/rules/local-work-artifacts.md +33 -0
- package/package/corpus/rules/managing-ai-rules.md +40 -0
- package/package/corpus/rules/orleans.md +50 -0
- package/package/corpus/rules/pull-requests.md +78 -0
- package/package/corpus/rules/react.md +195 -0
- package/package/corpus/rules/reactors.md +238 -0
- package/package/corpus/rules/rtk.md +37 -0
- package/package/corpus/rules/specs.csharp.md +139 -0
- package/package/corpus/rules/specs.md +132 -0
- package/package/corpus/rules/specs.scenarios.csharp.md +172 -0
- package/package/corpus/rules/specs.typescript.md +139 -0
- package/package/corpus/rules/storybook.md +84 -0
- package/package/corpus/rules/terminal-commands.md +19 -0
- package/package/corpus/rules/typescript.md +149 -0
- package/package/corpus/rules/verification-discipline.md +21 -0
- package/package/corpus/rules/vertical-slices.md +338 -0
- package/package/corpus/rules/web-fetching.md +11 -0
- package/package/corpus/rules/writing-correct-examples.md +35 -0
- package/package/corpus/rules/writing-cratis-docs.md +70 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/SKILL.md +131 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/code-style.md +187 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/domain-philosophy.md +91 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/exceptions-logging-and-di.md +223 -0
- package/package/corpus/skills/cratis-engineering-decision-record/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-decision-record/SKILL.md +133 -0
- package/package/corpus/skills/cratis-engineering-decision-record/references/record-format.md +107 -0
- package/package/corpus/skills/cratis-engineering-docs-authoring/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-docs-authoring/SKILL.md +86 -0
- package/package/corpus/skills/cratis-engineering-docs-authoring/references/site-format.md +46 -0
- package/package/corpus/skills/cratis-engineering-effect-boundaries/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-effect-boundaries/SKILL.md +130 -0
- package/package/corpus/skills/cratis-engineering-effect-boundaries/references/failure-archetypes.md +133 -0
- package/package/corpus/skills/cratis-fundamentals-concept/verification.json +8 -0
- package/package/profile-catalog.json +821 -0
- package/package.json +35 -13
- package/src/index.ts +80 -0
- /package/{skills → package/corpus/skills}/cratis-application-react-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-application-react-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-application-slice-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-application-slice-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/authentication.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/authorization.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/frontend.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/local-development.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/command-result.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/handler-shapes.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/proxy-generation.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/read-model-injection.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-execution/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-execution/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-validation/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-validation/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-ef-core-migration/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-ef-core-migration/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-observable-query-http/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-observable-query-http/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-query-paging/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-query-paging/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/data-page.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/data-tables.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/dialogs.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/mvvm.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/queries-and-commands.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-cli-operations/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-cli-operations/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-dotnet/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-dotnet/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-elixir/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-elixir/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-kotlin/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-kotlin/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-typescript/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-typescript/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-compliance/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-compliance/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-constraints/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-constraints/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-modeling/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-modeling/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-type-migration/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-type-migration/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/references/blocked-tools.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/references/observational-tools.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-multi-tenancy/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-multi-tenancy/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/references/fluent-builder.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/references/model-bound-attributes.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reactor/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reactor/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/references/queries.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reducer/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reducer/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-web-workbench/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-web-workbench/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-cli-terminal-workbench/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-cli-terminal-workbench/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-code-review/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-code-review/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-accessibility/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-accessibility/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-schema-editor/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-schema-editor/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-stepper-command-dialog/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-stepper-command-dialog/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-styling/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-styling/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-toolbar/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-toolbar/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-documentation-writing/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-documentation-writing/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-event-model-diagram/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-event-model-diagram/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-concept/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-concept/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-type-discovery/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-type-discovery/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-governed-release-methodology/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-governed-release-methodology/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-lens-browser-extension/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-lens-browser-extension/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-performance-review/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-performance-review/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-screenplay-model-authoring/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-screenplay-model-authoring/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-security-review/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-security-review/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specification-by-example/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-specification-by-example/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/application-scenarios.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/csharp-patterns.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/integration-specs.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-typescript/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-typescript/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-typescript/references/typescript-patterns.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-stage-rendering-and-sandbox/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-stage-rendering-and-sandbox/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/references/blocked-tools.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/references/observational-tools.md +0 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Exceptions, logging, and dependency injection
|
|
2
|
+
|
|
3
|
+
## Exceptions
|
|
4
|
+
|
|
5
|
+
Every exception type communicates *what went wrong in domain terms*. A built-in
|
|
6
|
+
`InvalidOperationException` says nothing; a domain `AuthorAlreadyRegistered`
|
|
7
|
+
says everything.
|
|
8
|
+
|
|
9
|
+
- Throw only for genuinely exceptional situations, never for control flow.
|
|
10
|
+
- Always define a custom type deriving from `Exception`. Never throw a built-in
|
|
11
|
+
exception type.
|
|
12
|
+
- Never suffix the type name with `Exception`.
|
|
13
|
+
- Always supply a meaningful message.
|
|
14
|
+
- Document the type with an XML summary starting "The exception that is thrown
|
|
15
|
+
when …".
|
|
16
|
+
- Never write an empty or silently swallowing `catch`. Handle it, log it, or let
|
|
17
|
+
it propagate. When ignoring is genuinely correct, use an exception filter
|
|
18
|
+
(`catch (<ExceptionType>) when (<condition>)`) whose body states the decision
|
|
19
|
+
through a comment or a fallback — never a bare `catch { }`.
|
|
20
|
+
|
|
21
|
+
```csharp
|
|
22
|
+
/// <summary>
|
|
23
|
+
/// The exception that is thrown when <condition>.
|
|
24
|
+
/// </summary>
|
|
25
|
+
/// <param name="<parameterName>">The <see cref="<ParameterType>"/> that <description>.</param>
|
|
26
|
+
public class <DomainExceptionName>(<ParameterType> <parameterName>)
|
|
27
|
+
: Exception($"<message> '{<parameterName>}' <detail>");
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```csharp
|
|
31
|
+
var <identifier> = await <source>.<FindMethod>(<argument>)
|
|
32
|
+
?? throw new <DomainExceptionName>(<argument>);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Logging
|
|
36
|
+
|
|
37
|
+
- Use structured logging with named parameters.
|
|
38
|
+
- Inject `ILogger<T>` where `T` is the containing class.
|
|
39
|
+
- Keep message definitions in a separate `<ClassName>Logging.cs` file as a
|
|
40
|
+
`static partial` internal class.
|
|
41
|
+
- Use the `[LoggerMessage]` attribute and do **not** supply an `eventId`.
|
|
42
|
+
- Choose the level deliberately: `Information`, `Warning`, `Error`, `Debug`.
|
|
43
|
+
|
|
44
|
+
```csharp
|
|
45
|
+
// <ClassName>Logging.cs
|
|
46
|
+
namespace <RootNamespace>.<Feature>;
|
|
47
|
+
|
|
48
|
+
static partial class <ClassName>Logging
|
|
49
|
+
{
|
|
50
|
+
[LoggerMessage(LogLevel.Information, "<message> '{<Parameter>}'")]
|
|
51
|
+
internal static partial void <MessageName>(
|
|
52
|
+
this ILogger<<ClassName>> logger, <ParameterType> <parameter>);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```csharp
|
|
57
|
+
public class <ClassName>(ILogger<<ClassName>> logger)
|
|
58
|
+
{
|
|
59
|
+
public Task <MethodName>(<ParameterType> <parameter>)
|
|
60
|
+
{
|
|
61
|
+
logger.<MessageName>(<parameter>);
|
|
62
|
+
<statement>;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Dependency injection
|
|
68
|
+
|
|
69
|
+
The framework discovers and wires dependencies by convention. Explicit
|
|
70
|
+
registration is the exception, not the rule.
|
|
71
|
+
|
|
72
|
+
- Prefer constructor injection. Never inject `IServiceProvider` to resolve
|
|
73
|
+
collaborators — that is the service-locator anti-pattern.
|
|
74
|
+
- Mark a singleton with the `[Singleton]` attribute rather than registering it
|
|
75
|
+
explicitly.
|
|
76
|
+
- A convention-based `IFoo → Foo` pair needs no registration.
|
|
77
|
+
- Command and query `Handle()` parameters resolve from DI by type.
|
|
78
|
+
|
|
79
|
+
```csharp
|
|
80
|
+
// Preferred — constructor injection
|
|
81
|
+
public class <ClassName>(<ICollaboratorType> <collaborator>);
|
|
82
|
+
|
|
83
|
+
// Avoid — service locator
|
|
84
|
+
public class <ClassName>(IServiceProvider provider)
|
|
85
|
+
{
|
|
86
|
+
void <MethodName>() =>
|
|
87
|
+
provider.GetService<<ICollaboratorType>>()!.<Method>();
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Service lifetimes — `[Singleton]` is a narrow choice
|
|
92
|
+
|
|
93
|
+
**Assume every application is multi-tenant**, even when it ships with a single
|
|
94
|
+
tenant and no tenant resolution configured. A single-tenant application is a
|
|
95
|
+
multi-tenant one with one tenant in it, and the code shape that serves both is
|
|
96
|
+
the same. The shape that serves only one has to be found and rewritten later,
|
|
97
|
+
from the far side of a data migration, in production.
|
|
98
|
+
|
|
99
|
+
That gives one rule with two faces:
|
|
100
|
+
|
|
101
|
+
> **A singleton may not depend on anything that belongs to a tenant, a user, or
|
|
102
|
+
> a request.**
|
|
103
|
+
|
|
104
|
+
These resolve **per scope**, and the scope carries the tenant, so none may be
|
|
105
|
+
injected into a `[Singleton]`:
|
|
106
|
+
|
|
107
|
+
| Off limits in a singleton | Why |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| The scoped event store and everything reached from it — event log, read models, constraints, event types, projections, reducers, PII | Resolved for the scope's namespace |
|
|
110
|
+
| A MongoDB collection, database, or client | The database name resolves per scope from the current tenant |
|
|
111
|
+
| An EF Core `DbContext` | Scoped for the same reason, and not thread-safe |
|
|
112
|
+
| A read model injected directly by key | Same scope, same binding |
|
|
113
|
+
| Any held tenant, principal, claims, correlation id, or HTTP context **value** | Belongs to one request and would outlive it |
|
|
114
|
+
|
|
115
|
+
A `[Singleton]` taking one of these is a **captive dependency**: the container
|
|
116
|
+
hands it the *root* scope's instance and keeps it for process lifetime. The root
|
|
117
|
+
scope has no request, so it resolves no tenant — every read and write goes to
|
|
118
|
+
the default namespace forever, regardless of who is asking.
|
|
119
|
+
|
|
120
|
+
**It does not throw. It returns nothing.** A query against the wrong namespace
|
|
121
|
+
hits a database that exists and is empty, so the caller receives an empty
|
|
122
|
+
collection, a null read model, or a default-valued options object and carries
|
|
123
|
+
on. The application starts, pages render, the build is green, and configuration
|
|
124
|
+
a tenant spent an afternoon entering is simply absent. It is invisible while
|
|
125
|
+
there is one tenant; every symptom appears the day a second arrives.
|
|
126
|
+
|
|
127
|
+
**What to use instead.** Default to the convention (transient), which inherits
|
|
128
|
+
the resolving scope's tenant for free, or a scoped lifetime when a service must
|
|
129
|
+
be shared within one request. Reserve `[Singleton]` for what is genuinely
|
|
130
|
+
process-wide and holds no tenant-, user-, or request-bound state: implementation
|
|
131
|
+
aggregators, HTTP client wrappers, options readers, pure computation, framework
|
|
132
|
+
plumbing.
|
|
133
|
+
|
|
134
|
+
When something must be a singleton and still needs data — a hosted service, a
|
|
135
|
+
dispatcher, a poller — inject `IServiceScopeFactory` and open a scope per unit
|
|
136
|
+
of work:
|
|
137
|
+
|
|
138
|
+
```csharp
|
|
139
|
+
// Wrong — the scoped collaborator captures the root scope's default namespace forever
|
|
140
|
+
[Singleton]
|
|
141
|
+
public class <ClassName>(<IScopedCollaboratorType> <collaborator>) : <IInterfaceName>
|
|
142
|
+
{
|
|
143
|
+
public Task<<ResultType>?> <MethodName>() => <collaborator>.<Method>(<argument>);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Right — a scope per call, so collaborators bind to the caller's tenant
|
|
147
|
+
[Singleton]
|
|
148
|
+
public class <ClassName>(IServiceScopeFactory scopeFactory) : <IInterfaceName>
|
|
149
|
+
{
|
|
150
|
+
public async Task<<ResultType>?> <MethodName>()
|
|
151
|
+
{
|
|
152
|
+
using var scope = scopeFactory.CreateScope();
|
|
153
|
+
var <collaborator> = scope.ServiceProvider
|
|
154
|
+
.GetRequiredService<<IScopedCollaboratorType>>();
|
|
155
|
+
|
|
156
|
+
return await <collaborator>.<Method>(<argument>);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
A client that names its store and namespace explicitly **is** singleton-safe,
|
|
162
|
+
and is the right collaborator when a flow knows which namespace it means and has
|
|
163
|
+
no scope to resolve one from. Naming the namespace is a deliberate, readable
|
|
164
|
+
statement that this code crosses a tenant boundary; capturing a scoped service
|
|
165
|
+
is the same crossing made by accident.
|
|
166
|
+
|
|
167
|
+
**The current user is not process-wide either.** Never keep the signed-in user,
|
|
168
|
+
their principal, claims, roles, or anything derived from them in a singleton.
|
|
169
|
+
The distinction that matters: *the accessor is fine, the value is not.* An HTTP
|
|
170
|
+
context accessor is itself a singleton and safe to inject; reading a value out
|
|
171
|
+
of it once and keeping it is not. A current-user service may be a singleton only
|
|
172
|
+
when every method reads through the accessor on each call and stores nothing.
|
|
173
|
+
Anything that derives something per user and wants to keep it holds a cache
|
|
174
|
+
**keyed by the user**, never a single field.
|
|
175
|
+
|
|
176
|
+
**Off-request work carries its tenant.** Reactors, hosted services, background
|
|
177
|
+
dispatch, and scheduled jobs run with no request, so a tenant resolver has
|
|
178
|
+
nothing to read. Observers may be instantiated per namespace, but the
|
|
179
|
+
collaborators they call are not — a flow that reaches a tenant-blind singleton
|
|
180
|
+
has left its namespace behind without saying so. Such a flow states its tenant
|
|
181
|
+
explicitly rather than inheriting whatever the root scope happens to be.
|
|
182
|
+
|
|
183
|
+
## Discovering implementations — `IInstancesOf<T>`, never `IEnumerable<T>`
|
|
184
|
+
|
|
185
|
+
When a type needs every implementation of an abstraction — handlers, strategies,
|
|
186
|
+
filters, validators, formatters — inject `IInstancesOf<TInterface>` from
|
|
187
|
+
`Cratis.Types`. The framework discovers and instantiates every implementation by
|
|
188
|
+
convention, so no explicit registration exists anywhere.
|
|
189
|
+
|
|
190
|
+
```csharp
|
|
191
|
+
// Wrong — hand-maintained registrations. A new implementation added elsewhere
|
|
192
|
+
// silently does nothing until someone remembers this file, and dead
|
|
193
|
+
// registrations linger after types are removed.
|
|
194
|
+
services.AddSingleton<<IHandlerType>, <FirstHandler>>();
|
|
195
|
+
services.AddSingleton<<IHandlerType>, <SecondHandler>>();
|
|
196
|
+
|
|
197
|
+
public class <AggregatorName>(IEnumerable<<IHandlerType>> handlers) : <IAggregatorType>;
|
|
198
|
+
|
|
199
|
+
// Right — implementations discovered automatically
|
|
200
|
+
[Singleton]
|
|
201
|
+
public class <FirstHandler>(<CollaboratorType> <collaborator>) : <IHandlerType>;
|
|
202
|
+
|
|
203
|
+
[Singleton]
|
|
204
|
+
public class <SecondHandler>(<CollaboratorType> <collaborator>) : <IHandlerType>;
|
|
205
|
+
|
|
206
|
+
[Singleton]
|
|
207
|
+
public class <AggregatorName>(IInstancesOf<<IHandlerType>> handlers) : <IAggregatorType>;
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Rules:
|
|
211
|
+
|
|
212
|
+
- Never inject `IEnumerable<TInterface>` to enumerate implementations of an
|
|
213
|
+
abstraction. That signature works only when every implementation is
|
|
214
|
+
hand-registered, which defeats convention-based discovery.
|
|
215
|
+
- Never register a type that exists to be discovered. Mark it `[Singleton]`, or
|
|
216
|
+
rely on the `IFoo → Foo` convention for a transient, and delete the
|
|
217
|
+
registration line.
|
|
218
|
+
- `IInstancesOf<T>` resolves at the point of access, so an implementation added
|
|
219
|
+
later in the assembly becomes available without touching the consumer or any
|
|
220
|
+
composition root.
|
|
221
|
+
- `IEnumerable<T>` remains the right type to **return** from a method that
|
|
222
|
+
yields a sequence of values. The rule applies only to enumerating
|
|
223
|
+
*implementations* of an abstraction.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-engineering-decision-record
|
|
3
|
+
description: Consult, author, accept, and supersede decision records in a Cratis repository's decisions/ folder. Use before an architectural, contract, scope, or cross-cutting change, when a ruling has been made that later work must obey, or when an accepted decision has to be replaced. Defer product documentation, session handovers, and work-item status to their own workflows.
|
|
4
|
+
license: LICENSE
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cratis decision records
|
|
8
|
+
|
|
9
|
+
A decision is a durable choice with a **decider** and a **date**. It is
|
|
10
|
+
documentation, not a work record: it lives in the repository's `decisions/`
|
|
11
|
+
folder and is reviewed like any other documentation. A handover may summarize a
|
|
12
|
+
decision; it never holds the only copy.
|
|
13
|
+
|
|
14
|
+
This skill owns the *procedure* — how to consult, author, accept, and supersede
|
|
15
|
+
a record. It does not decide what to decide, and it never grants acceptance.
|
|
16
|
+
|
|
17
|
+
## When you need this
|
|
18
|
+
|
|
19
|
+
- You are about to make an architectural, contract, scope, or cross-cutting
|
|
20
|
+
change. Consult first: a decision you did not read still binds the change.
|
|
21
|
+
- A ruling was made — in review, in chat, in a meeting — that later work has to
|
|
22
|
+
obey. Record it in the same turn, while the reasoning is still available.
|
|
23
|
+
- An accepted decision no longer holds and has to be replaced, narrowed, or
|
|
24
|
+
qualified.
|
|
25
|
+
- Your change would contradict an accepted record. Stop: supersession or a human
|
|
26
|
+
verdict comes first, never a workaround.
|
|
27
|
+
|
|
28
|
+
## When you do not
|
|
29
|
+
|
|
30
|
+
- **Session notes, plans, handovers, status boards.** Those are work records.
|
|
31
|
+
They belong in the repository's ignored local working directory, never in
|
|
32
|
+
`decisions/`.
|
|
33
|
+
- **Product or API documentation.** A record says what was chosen and why; the
|
|
34
|
+
documentation says how the thing works. Use the documentation workflow.
|
|
35
|
+
- **A work item's status.** "Blocked on X" is a work item field, not a decision.
|
|
36
|
+
- **A reversible choice inside your own scope that nobody will re-litigate.**
|
|
37
|
+
Make it and move on; see the significance test in step 2.
|
|
38
|
+
- **A decision this repository does not own.** Company-level and portfolio
|
|
39
|
+
decisions live in the record set that owns them. Cite that id; do not copy the
|
|
40
|
+
record into a repository that cannot supersede it.
|
|
41
|
+
|
|
42
|
+
## Steps
|
|
43
|
+
|
|
44
|
+
1. **List the records in force for the paths you are changing.** Read
|
|
45
|
+
`decisions/`, keep the records whose `applies-to` matches a path you are
|
|
46
|
+
about to touch and whose `status` is `accepted`, and order them newest first.
|
|
47
|
+
Report the count — "0 records matched" and "3 matched, none contradicted" are
|
|
48
|
+
different verdicts and must read differently.
|
|
49
|
+
2. **Cite what you relied on.** Name the ids on the work item, in the pull
|
|
50
|
+
request body, and as a `Decision: <id>` commit trailer. A change that
|
|
51
|
+
silently contradicts an accepted record is a defect even when the code is
|
|
52
|
+
correct.
|
|
53
|
+
3. **Apply the significance test before writing anything.** Write a record only
|
|
54
|
+
when at least one of these holds: someone will otherwise re-litigate the
|
|
55
|
+
choice; it binds paths beyond the one you are changing; reversing it would
|
|
56
|
+
cost real migration or rework; or it rejects an option a reasonable reader
|
|
57
|
+
would reach for. If none holds, say so and make the change without a record.
|
|
58
|
+
4. **Pass the completeness gate, or open with `status: returned`.** A proposed
|
|
59
|
+
record states the options considered *including the one not taken and why*,
|
|
60
|
+
the default that applies if the question is never answered and what that
|
|
61
|
+
default costs, the timeline the decision has to hold to, and what is in scope
|
|
62
|
+
and out. A record missing any of the four is returned to its proposer for
|
|
63
|
+
revision — `returned` is not a rejection.
|
|
64
|
+
5. **Write the verification criterion before acceptance, not after.** State the
|
|
65
|
+
observable signal that will say the decision was actually carried out, as
|
|
66
|
+
`Done when` and `Verify by`. A decision whose success cannot be observed
|
|
67
|
+
cannot reach `stage: verified`.
|
|
68
|
+
6. **Open the record as `status: proposed`, `stage: none`, and regenerate the
|
|
69
|
+
index.** A record the index does not list is a record the consult step in
|
|
70
|
+
step 1 will never find.
|
|
71
|
+
7. **Accept by recording a resolved actor and a date.** Set `status: accepted`,
|
|
72
|
+
`decided` to the date, and `decider` to a named person — never a role, a
|
|
73
|
+
team, or a tool. Acceptance is a human verdict: draft it, do not grant it.
|
|
74
|
+
8. **Spawn the build work carrying the criterion verbatim.** The `Done when` and
|
|
75
|
+
`Verify by` text written in step 5 travels onto the work item unchanged, so
|
|
76
|
+
the thing that gets built is the thing that was decided.
|
|
77
|
+
9. **Move `stage` only on the evidence the next stage requires.** `none` →
|
|
78
|
+
`implemented` when the change exists in the tree; `implemented` → `verified`
|
|
79
|
+
only on a signal observed this time. Accepted is not implemented, and
|
|
80
|
+
implemented is not verified.
|
|
81
|
+
10. **Supersede rather than rewrite.** Never edit an accepted record's decision
|
|
82
|
+
text in place — that text is what people relied on. Correct a typo or add
|
|
83
|
+
context under a dated banner that says what changed and why. Change the
|
|
84
|
+
*choice* only with a new record.
|
|
85
|
+
11. **Point both ways and sweep the citations.** The new record names the one it
|
|
86
|
+
replaces in `supersedes`; the replaced record's `status` becomes
|
|
87
|
+
`superseded` and it gains a `superseded-by` pointer forward, with its
|
|
88
|
+
original text preserved. Then find every work item, pull request body, and
|
|
89
|
+
commit trailer citing the old id and point it at the new one. A reader
|
|
90
|
+
arriving at either record must be able to reach the other.
|
|
91
|
+
|
|
92
|
+
The exact front-matter fields, the closed value sets, and the index shape are in
|
|
93
|
+
[record-format.md](references/record-format.md).
|
|
94
|
+
|
|
95
|
+
## What breaks
|
|
96
|
+
|
|
97
|
+
- **A role in the `decider` field.** "The architecture team decided" names
|
|
98
|
+
nobody who can be asked what they meant or who can supersede it. The record
|
|
99
|
+
reads as authority but resolves to no one.
|
|
100
|
+
- **Decision text edited in place.** The next reader sees text nobody ever
|
|
101
|
+
agreed to, and the people who relied on the old wording have no way to tell
|
|
102
|
+
what changed. This is the failure that makes a whole `decisions/` folder
|
|
103
|
+
untrustworthy, because it is invisible.
|
|
104
|
+
- **A one-way supersession.** The new record says it supersedes the old one, but
|
|
105
|
+
the old one still reads as accepted. Whoever arrives from a search, a
|
|
106
|
+
citation, or an old pull request follows a decision that was replaced.
|
|
107
|
+
- **`stage: verified` set on a green build.** Compilation proves it builds, not
|
|
108
|
+
that the decision was carried out. The stage then lies about the only thing it
|
|
109
|
+
exists to say.
|
|
110
|
+
- **A ruling that stayed in chat.** It binds the next change and nobody can find
|
|
111
|
+
it. The symptom is the same argument being had a second time, with a different
|
|
112
|
+
outcome.
|
|
113
|
+
- **One record settling three questions.** It cannot be superseded for one of
|
|
114
|
+
them, so it survives past the point where a third of it is wrong.
|
|
115
|
+
- **An `applies-to` that matches nothing.** Step 1 returns zero records and reads
|
|
116
|
+
as "nothing binds this change" instead of "the glob is wrong". Report the count
|
|
117
|
+
so an empty result is visible rather than reassuring.
|
|
118
|
+
|
|
119
|
+
## How it is proven
|
|
120
|
+
|
|
121
|
+
- **Consult ran and found something specific.** The count from step 1 appears in
|
|
122
|
+
the report, and the ids it returned appear on the work item, in the pull
|
|
123
|
+
request body, and in a `Decision:` commit trailer.
|
|
124
|
+
- **Acceptance resolves.** The record carries a `decided` date and a `decider`
|
|
125
|
+
that names a person you could actually ask.
|
|
126
|
+
- **Supersession is traversable.** Follow `superseded-by` forward and
|
|
127
|
+
`supersedes` back; both land on the other record. Search the repository for
|
|
128
|
+
the superseded id and confirm no live citation still points only at it.
|
|
129
|
+
- **The stage matches the evidence.** `implemented` is confirmed by the change
|
|
130
|
+
being in the tree; `verified` is confirmed by naming the signal — the command,
|
|
131
|
+
the gate, the observed behavior — that was watched *this time*.
|
|
132
|
+
- **The index resolves.** Every record in `decisions/` appears in the index, and
|
|
133
|
+
every index entry resolves to a file.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Decision record format
|
|
2
|
+
|
|
3
|
+
The shape below is the one Cratis repositories that keep a `decisions/` folder
|
|
4
|
+
converge on. A repository that already defines a stricter local shape stays
|
|
5
|
+
authoritative; add fields there rather than dropping the ones listed here.
|
|
6
|
+
|
|
7
|
+
## Front matter
|
|
8
|
+
|
|
9
|
+
| Field | Required | Meaning |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `id` | Yes | Stable identifier, unique in the repository, never reused after supersession. |
|
|
12
|
+
| `title` | Yes | The single question the record settles, stated as a choice. |
|
|
13
|
+
| `status` | Yes | Where the record stands in its own review lifecycle. Closed set below. |
|
|
14
|
+
| `stage` | Yes | How far an accepted decision has travelled from words into observed behavior. Closed set below. |
|
|
15
|
+
| `class` | Yes | What kind of choice this is, which sets who may settle it. Closed set below. |
|
|
16
|
+
| `reversibility` | Yes | What undoing it would cost. Closed set below. |
|
|
17
|
+
| `decided` | On acceptance | The date the decision was accepted. |
|
|
18
|
+
| `decider` | On acceptance | A named person. Never a role, a team, or a tool. |
|
|
19
|
+
| `applies-to` | Yes | The paths this record binds, as globs. What the consult step matches against. |
|
|
20
|
+
| `supersedes` | When replacing | The id of the record this one replaces. |
|
|
21
|
+
| `superseded-by` | When replaced | The id of the record that replaced this one. |
|
|
22
|
+
|
|
23
|
+
`status` and `superseded-by` move together: a record marked `superseded` without
|
|
24
|
+
a forward pointer strands every reader who arrives at it.
|
|
25
|
+
|
|
26
|
+
## Closed value sets
|
|
27
|
+
|
|
28
|
+
Do not invent a word for a state one of these sets already names.
|
|
29
|
+
|
|
30
|
+
**`status`**
|
|
31
|
+
|
|
32
|
+
| Value | Meaning |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `proposed` | Written and offered for a verdict; not yet in force. |
|
|
35
|
+
| `returned` | Sent back to the proposer for revision; not a rejection. |
|
|
36
|
+
| `accepted` | In force; binding on work that touches the paths it covers. |
|
|
37
|
+
| `rejected` | Refused; the choice it proposed is not taken. |
|
|
38
|
+
| `deferred` | Deliberately not settled yet, with the reason recorded. |
|
|
39
|
+
| `superseded` | Replaced by a later record, which it points at. |
|
|
40
|
+
|
|
41
|
+
**`stage`**
|
|
42
|
+
|
|
43
|
+
| Value | Meaning |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `none` | Accepted, but nothing has been built against it yet. |
|
|
46
|
+
| `implemented` | The change the decision calls for exists in the tree. |
|
|
47
|
+
| `verified` | A signal observed this time confirms the implementation. |
|
|
48
|
+
|
|
49
|
+
**`class`**
|
|
50
|
+
|
|
51
|
+
| Value | Meaning |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `strategy` | Direction, portfolio, or ownership of a body of work. |
|
|
54
|
+
| `contract` | An interface, schema, protocol, or release boundary others build on. |
|
|
55
|
+
| `product` | What is built, for whom, and what it promises. |
|
|
56
|
+
| `working` | A local, reversible choice inside one team's own scope. |
|
|
57
|
+
|
|
58
|
+
**`reversibility`**
|
|
59
|
+
|
|
60
|
+
| Value | Meaning |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| `reversible` | Undone at negligible cost; decide fast and revisit. |
|
|
63
|
+
| `costly` | Undone, but only by paying real migration or rework cost. |
|
|
64
|
+
| `irreversible` | Cannot be undone; requires a human verdict before acting. |
|
|
65
|
+
|
|
66
|
+
`class` and `reversibility` together say who may settle the record. A `strategy`
|
|
67
|
+
or `irreversible` record is never accepted by an agent.
|
|
68
|
+
|
|
69
|
+
## Body sections
|
|
70
|
+
|
|
71
|
+
A record's body carries, in this order:
|
|
72
|
+
|
|
73
|
+
1. **Context** — the situation that forced a choice, and what changes if nobody
|
|
74
|
+
chooses.
|
|
75
|
+
2. **Decision** — the choice, in one paragraph, in the present tense. This is the
|
|
76
|
+
text that is never edited in place once the record is accepted.
|
|
77
|
+
3. **Options considered** — including the one not taken and why. This is the part
|
|
78
|
+
a future reader needs most and the part nobody remembers.
|
|
79
|
+
4. **Default if unanswered** — what happens if the question is never settled, and
|
|
80
|
+
what that costs. A record without this cannot be weighed against doing nothing.
|
|
81
|
+
5. **Timeline and scope** — the horizon the decision holds to, what is in scope,
|
|
82
|
+
and what is explicitly out.
|
|
83
|
+
6. **Verification** — `Done when` and `Verify by`, written before acceptance. The
|
|
84
|
+
observable signal that says the decision was carried out.
|
|
85
|
+
7. **Consequences** — what this makes easier, what it makes harder, and what it
|
|
86
|
+
forecloses.
|
|
87
|
+
|
|
88
|
+
## Corrections after acceptance
|
|
89
|
+
|
|
90
|
+
A typo fix or added context goes under a dated banner inside the record:
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
> **2026-03-04 — clarification.** The decision text below said "client"; every
|
|
94
|
+
> use of that word means the generated client SDK, not a consuming application.
|
|
95
|
+
> The choice itself is unchanged.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Anything that changes the choice is a new record with two-way pointers, not a
|
|
99
|
+
banner.
|
|
100
|
+
|
|
101
|
+
## Index
|
|
102
|
+
|
|
103
|
+
The folder carries an index listing every record with its id, title, status,
|
|
104
|
+
stage, decided date, and decider. The index is regenerated whenever a record is
|
|
105
|
+
added or its status changes; a record the index omits is a record the consult
|
|
106
|
+
step will never find. Two checks keep it honest: every file in the folder appears
|
|
107
|
+
in the index, and every index entry resolves to a file.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-engineering-docs-authoring
|
|
3
|
+
description: Draft accurate Cratis documentation content after the owning repository, page placement, document type, and authoritative product sources are known. Use for tutorials, how-to guides, explanations, and references; defer placement, existing-page discovery, and visual QA to their companion workflows.
|
|
4
|
+
license: LICENSE
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cratis documentation authoring
|
|
8
|
+
|
|
9
|
+
Draft one accurate Cratis documentation page in the voice and structure required
|
|
10
|
+
by its document type. This skill owns **content**. It does not decide which
|
|
11
|
+
repository owns a page, wire site navigation, locate an existing source page, or
|
|
12
|
+
perform visual QA.
|
|
13
|
+
|
|
14
|
+
## Required inputs
|
|
15
|
+
|
|
16
|
+
Before drafting, establish:
|
|
17
|
+
|
|
18
|
+
- the owning repository and destination page;
|
|
19
|
+
- document type: tutorial, how-to, explanation, or reference;
|
|
20
|
+
- target reader and the outcome they need;
|
|
21
|
+
- authoritative product source for every API, command, version, and capability;
|
|
22
|
+
- explicit scope and important exclusions.
|
|
23
|
+
|
|
24
|
+
Use repository evidence to resolve routine details. Ask only when materially
|
|
25
|
+
different document types, audiences, or product choices remain plausible.
|
|
26
|
+
|
|
27
|
+
## Route near misses
|
|
28
|
+
|
|
29
|
+
- New-page placement or navigation is unresolved: defer to
|
|
30
|
+
`cratis-engineering-docs-add-page`.
|
|
31
|
+
- The request changes an existing page whose source location is unresolved:
|
|
32
|
+
defer to `cratis-engineering-docs-edit-page`.
|
|
33
|
+
- The request is to render, screenshot, or diagnose visual layout: defer to
|
|
34
|
+
`cratis-engineering-docs-visual-qa`.
|
|
35
|
+
- A product/API claim lacks first-party source evidence: stop and identify the
|
|
36
|
+
missing authority instead of drafting the claim.
|
|
37
|
+
- The subject is not Cratis product or engineering documentation: do not apply
|
|
38
|
+
this skill.
|
|
39
|
+
|
|
40
|
+
## Write one document type
|
|
41
|
+
|
|
42
|
+
Do not mix Diátaxis types on one page:
|
|
43
|
+
|
|
44
|
+
| Type | Reader need | Shape |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| Tutorial | Learn by completing a guided outcome | Ordered steps with visible results |
|
|
47
|
+
| How-to | Solve one concrete problem | Prerequisites, direct procedure, completion check |
|
|
48
|
+
| Explanation | Understand why and when | Concepts, boundaries, trade-offs, diagram |
|
|
49
|
+
| Reference | Look up exact information | Exhaustive tables, fields, commands, signatures |
|
|
50
|
+
|
|
51
|
+
For the detailed mechanical format, read
|
|
52
|
+
[site-format.md](references/site-format.md).
|
|
53
|
+
|
|
54
|
+
## Drafting workflow
|
|
55
|
+
|
|
56
|
+
1. Open with the reader's concrete friction and the Cratis capability that
|
|
57
|
+
relieves it.
|
|
58
|
+
2. Organize by the reader's workflow, not by implementation namespaces or an
|
|
59
|
+
alphabetical API dump.
|
|
60
|
+
3. Use active voice, present tense, second person, and American English.
|
|
61
|
+
4. Explain the invisible behavior after each example: what the framework does
|
|
62
|
+
and why the boundary matters.
|
|
63
|
+
5. Verify every API and command against first-party source at the applicable
|
|
64
|
+
revision. Never translate a C# example into another client language by guess.
|
|
65
|
+
6. State maturity, authorization, side effects, unsupported surfaces, and when a
|
|
66
|
+
simpler approach is better.
|
|
67
|
+
7. Show a visible result in tutorials and procedures. Use Mermaid for a
|
|
68
|
+
non-trivial explanation.
|
|
69
|
+
8. End with the natural next page or workflow.
|
|
70
|
+
|
|
71
|
+
## Correctness boundary
|
|
72
|
+
|
|
73
|
+
Never invent product APIs, customer claims, versions, support commitments,
|
|
74
|
+
marketplace availability, or private implementation details. Do not copy a code
|
|
75
|
+
sample from memory. If the source cannot prove a claim, omit it or mark the gap
|
|
76
|
+
for the owning maintainer.
|
|
77
|
+
|
|
78
|
+
A successful build proves rendering, not technical correctness. The owning
|
|
79
|
+
repository still runs its documentation, snippet, link, and product gates.
|
|
80
|
+
|
|
81
|
+
## Output
|
|
82
|
+
|
|
83
|
+
Return or write the page content only at the already approved destination. Do
|
|
84
|
+
not modify navigation, generated copies, project context, credentials, package
|
|
85
|
+
manifests, or unrelated documentation. Report the authoritative source checked
|
|
86
|
+
and the verification that still remains.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Cratis documentation site format
|
|
2
|
+
|
|
3
|
+
Use these rules for a page that will render on the Cratis Astro Starlight site.
|
|
4
|
+
The owning repository remains authoritative when it defines a stricter format.
|
|
5
|
+
|
|
6
|
+
## Frontmatter and headings
|
|
7
|
+
|
|
8
|
+
- Include `title` and `description` frontmatter.
|
|
9
|
+
- Do not add a body H1; the site renders the title as H1.
|
|
10
|
+
- Start body sections at H2.
|
|
11
|
+
- Use sentence case and no trailing punctuation in headings.
|
|
12
|
+
- Keep the page's main workflow visible in H2 sections.
|
|
13
|
+
|
|
14
|
+
## Code and commands
|
|
15
|
+
|
|
16
|
+
- Tag every code fence with its language.
|
|
17
|
+
- Dedent copied snippets to their natural source indentation.
|
|
18
|
+
- Use complete, runnable examples without ellipses.
|
|
19
|
+
- Verify examples against first-party product source.
|
|
20
|
+
- Use the client-owned multi-language snippet mechanism when shared product docs
|
|
21
|
+
support more than one client; do not hand-translate unsupported clients.
|
|
22
|
+
|
|
23
|
+
## Links and navigation
|
|
24
|
+
|
|
25
|
+
- Use descriptive link text, never "here" or "read more."
|
|
26
|
+
- Use root-relative links between products.
|
|
27
|
+
- Keep site-level links extensionless.
|
|
28
|
+
- Preserve the owning product repository's source-link convention.
|
|
29
|
+
- Do not edit generated synchronized pages; edit the owning source repository.
|
|
30
|
+
|
|
31
|
+
## Tables, asides, and diagrams
|
|
32
|
+
|
|
33
|
+
- Use GitHub-Flavored Markdown tables with a spaced separator row.
|
|
34
|
+
- Use Starlight or owning-repository note/caution syntax for boundaries and
|
|
35
|
+
security warnings.
|
|
36
|
+
- Use Mermaid for architecture, sequence, or state explanations.
|
|
37
|
+
- Give images meaningful alternative text.
|
|
38
|
+
|
|
39
|
+
## File hygiene
|
|
40
|
+
|
|
41
|
+
- Use American English.
|
|
42
|
+
- End the file with one newline.
|
|
43
|
+
- Keep project paths, credentials, local endpoints, and private data out of
|
|
44
|
+
shared documentation.
|
|
45
|
+
- Run the owning repository's build, lint, snippet, and link checks before
|
|
46
|
+
calling the page complete.
|