| name | new-vertical-slice |
| description | Use this skill when asked to implement a new feature, command, query, slice, or screen in a Cratis-based project. Guides the full end-to-end workflow: C# backend → Debug+Release build → specs → React frontend → quality gates. |
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-concept skill)
- Command
record with Handle() 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() — see add-business-rule skill (DCB pattern)
CommandValidator<T> for command-level rejection rules (see add-business-rule); ConceptValidator<T> for value invariants
- Constraint class
<Name>Constraint (if needed)
- Event
record with [EventType] (no arguments, no mutable properties)
- Read model
record with [ReadModel] and model-bound projection attributes ([FromEvent<T>], [Key], etc.)
- Use fluent
IProjectionFor<T> only when model-bound attributes don't fit
Critical rules:
- Commands are
record types with a Handle() 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> (no Features segment — 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 — Release regenerates the TypeScript proxies; Debug compiles #if DEBUG spec code.
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
CommandDialog from @cratis/components/CommandDialog for command dialogs
- Use
Dialog from @cratis/components/Dialogs for data-only dialogs — NEVER import from primereact/dialog
- Use PrimeReact CSS variables for all colors — never hard-code hex values
- Use full descriptive variable names — never abbreviations (
event not e, index not idx)
- No
any types — use unknown with 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);
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/warnings
dotnet test — zero failures
yarn lint — zero errors
npx tsc -b — zero errors
- Public-facing changes (clients, SDKs, public APIs) include associated documentation updates
cd Documentation/web && npm run check passes when documentation is added or changed
For complete code patterns for all 4 slice types and frontend examples, see references/PATTERNS.md.