Implement a complete vertical slice following this EXACT order. Never skip steps or work on multiple slices in parallel.
Step 1 — Identify the slice type
Choose one of:
- State Change — a command that mutates state and records events (most common)
- State View — a query that reads from a read model
- Automation — a background reactor triggered by events
- Translation — transforms events into other events
Step 2 — Determine the namespace root
Read global.json and existing .cs files under the app source root to find the namespace root (e.g. Studio, Library). Never hard-code it.
Step 3 — Create the C# slice file
Place ALL backend artifacts in a single file in the slice folder: <Feature>/<Slice>/<Slice>.cs (under the app source root; a <Module>/ grouping above the feature is optional — there is no top-level Features/ wrapper).
File creation order within the slice:
- Concept types (if new strongly-typed IDs are needed — see
add-conceptskill) - Command
recordwithHandle()method and optional validation attributes- If a business rule depends on Chronicle event-sourced state, add the relevant read model as a parameter to
Handle()— seeadd-business-ruleskill (DCB pattern)
- If a business rule depends on Chronicle event-sourced state, add the relevant read model as a parameter to
CommandValidator<T>for command-level rejection rules (seeadd-business-rule);ConceptValidator<T>for value invariants- Constraint class
<Name>Constraint(if needed) - Event
recordwith[EventType](no arguments, no mutable properties) - Read model
recordwith[ReadModel]and model-bound projection attributes ([FromEvent<T>],[Key], etc.)- Use fluent
IProjectionFor<T>only when model-bound attributes don't fit
- Use fluent
Critical rules:
- Commands are
recordtypes with aHandle()method directly on them — DO NOT create separate handler classes - Events use
[EventType]with NO arguments — never pass a GUID or string - Projection: prefer model-bound attributes on the read model; if using
IProjectionFor<T>, AutoMap is on by default — just call.From<>()directly - Namespace mirrors the folder path under the source root:
<RootNamespace>.<Module>.<Feature>.<Slice>(noFeaturessegment — drop any level that isn't present) - Copyright header on every file:
// Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
Step 4 — Build
Run dotnet build in both Debug and Release. Fix ALL errors and warnings before proceeding — Debug regenerates the TypeScript proxies and compiles #if DEBUG spec code; build Release with -p:CratisProxiesOutputPath= to skip re-running proxy generation.
Step 5 — Write specs (mandatory for every slice type)
Use the in-process scenario family — CommandScenario for State Change, ReadModelScenario for State View, ReactorScenario for Automation/Translation. For each command, write specs covering:
- Happy path — command succeeds, correct event appended
- Each validation failure (one spec per rule)
- Each business rule violation (one spec per DCB condition in
Handle()that inspects a read model) - Each constraint violation
See write-specs skill for the complete spec structure.
Run dotnet test. Fix all failures before proceeding.
Step 6 — Implement React component(s)
Place .tsx files in the slice folder <Feature>/<Slice>/.
- Import the auto-generated command/query proxy from the same folder
- Use
CommandDialogfrom@cratis/components/CommandDialogfor command dialogs - Use
Dialogfrom@cratis/components/Dialogsfor data-only dialogs — NEVER import fromprimereact/dialog - Use PrimeReact CSS variables for all colors — never hard-code hex values
- Use full descriptive variable names — never abbreviations (
eventnote,indexnotidx) - No
anytypes — useunknownwith type guards
Command usage:
const [myCommand] = MyCommand.use();
const handleSubmit = async () => {
myCommand.propertyName = value;
const result = await myCommand.execute();
if (result.isSuccess) closeDialog(DialogResult.Ok);
};
Query with paging:
const pageSize = 10;
const [result, , setPage] = MyQuery.useWithPaging(pageSize);
// Use result.data, result.paging.totalItems, result.paging.page
Write specs for the React surface (view models, helpers, component behavior) with the write-specs-frontend skill.
Step 7 — Update the composition page
Open <Feature>/<Feature>.tsx and add the new component. If a new page is introduced, also update the router and navigation.
Step 8 — Quality gates
All must pass before the slice is considered done:
dotnet build— zero errors/warningsdotnet test— zero failuresyarn lint— zero errorsnpx tsc -b— zero errors- Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
cd Documentation/web && npm run checkpasses when documentation is added or changed
For complete code patterns for all 4 slice types and frontend examples, see references/PATTERNS.md.