@eventmodelers/cli 1.0.73 → 1.0.75
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/README.md +15 -8
- package/cli.js +47 -29
- package/package.json +2 -2
- package/shared/build-kit/README.md +26 -9
- package/shared/build-kit/lib/local-ai-agent.js +281 -0
- package/shared/build-kit/lib/ralph.js +1 -1
- package/shared/build-kit/ralph-local-ai.js +40 -0
- package/shared/build-kit/realtime-agent.js +1 -1
- package/shared/skills/learn-eventmodelers-api/SKILL.md +4 -2
- package/stacks/blank/templates/.claude/skills/build-automation/SKILL.md +1 -1
- package/stacks/blank/templates/.claude/skills/build-state-change/SKILL.md +1 -1
- package/stacks/blank/templates/.claude/skills/build-state-view/SKILL.md +1 -1
- package/stacks/blank/templates/root/README.md +1 -1
- package/stacks/bridge/templates/bridge/lib/AGENT.md +1 -1
- package/stacks/bridge/templates/bridge/ralph-local-ai.js +43 -0
- package/stacks/modeling-kit/templates/kit/README.md +1 -1
- package/stacks/react/templates/.claude/skills/build-automation/SKILL.md +1 -1
- package/stacks/react/templates/.claude/skills/build-state-change/SKILL.md +1 -1
- package/stacks/react/templates/.claude/skills/build-state-view/SKILL.md +1 -1
- package/stacks/react/templates/build-kit/README.md +26 -9
- package/stacks/react/templates/build-kit/lib/ralph.js +1 -1
- package/stacks/react/templates/build-kit/ralph-local-ai.js +40 -0
- package/shared/build-kit/lib/ollama-agent.js +0 -147
- package/shared/build-kit/ralph-ollama.js +0 -40
- package/stacks/bridge/templates/bridge/ralph-ollama.js +0 -43
- package/stacks/cratis-csharp/templates/.claude/skills/_shared/cratis-conventions.md +0 -251
- package/stacks/cratis-csharp/templates/.claude/skills/build-automation/SKILL.md +0 -122
- package/stacks/cratis-csharp/templates/.claude/skills/build-automation/references/patterns.md +0 -115
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/SKILL.md +0 -191
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/references/patterns.md +0 -234
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/SKILL.md +0 -149
- package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/references/patterns.md +0 -166
- package/stacks/cratis-csharp/templates/build-kit/CLAUDE.md +0 -78
- package/stacks/cratis-csharp/templates/build-kit/lib/AGENT.md +0 -59
- package/stacks/cratis-csharp/templates/build-kit/lib/backend-prompt.md +0 -140
- package/stacks/cratis-csharp/templates/build-kit/lib/prompt.md +0 -126
- package/stacks/cratis-csharp/templates/root/.frontend/index.css +0 -29
- package/stacks/cratis-csharp/templates/root/.frontend/index.html +0 -17
- package/stacks/cratis-csharp/templates/root/.frontend/main.tsx +0 -18
- package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.json +0 -42
- package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.node.json +0 -11
- package/stacks/cratis-csharp/templates/root/.frontend/vite.config.ts +0 -56
- package/stacks/cratis-csharp/templates/root/App.tsx +0 -23
- package/stacks/cratis-csharp/templates/root/CratisApp.csproj +0 -25
- package/stacks/cratis-csharp/templates/root/CratisApp.sln +0 -18
- package/stacks/cratis-csharp/templates/root/GlobalUsings.cs +0 -3
- package/stacks/cratis-csharp/templates/root/Home.tsx +0 -102
- package/stacks/cratis-csharp/templates/root/Program.cs +0 -26
- package/stacks/cratis-csharp/templates/root/README.md +0 -192
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/AllListings.ts +0 -47
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.cs +0 -11
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.ts +0 -12
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/ListingDataTable.tsx +0 -17
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/index.ts +0 -1
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Register.ts +0 -51
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/RegisterDialog.tsx +0 -18
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Registration.cs +0 -27
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/index.ts +0 -1
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeFeature.tsx +0 -22
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeName.cs +0 -3
- package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/index.ts +0 -1
- package/stacks/cratis-csharp/templates/root/appsettings.Development.json +0 -9
- package/stacks/cratis-csharp/templates/root/appsettings.json +0 -26
- package/stacks/cratis-csharp/templates/root/docker-compose.yml +0 -23
- package/stacks/cratis-csharp/templates/root/package.json +0 -33
- package/stacks/cratis-csharp/templates/root/tsconfig.json +0 -3
- package/stacks/react/templates/build-kit/ralph-ollama.js +0 -40
package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/references/patterns.md
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# Read Slice Patterns — Cratis Arc + Chronicle
|
|
2
|
-
|
|
3
|
-
Complete code for state-view (read) slices. All artifacts go in a single
|
|
4
|
-
`<Module>/<Feature>/<Slice>/<Slice>.cs` (the shipped starter uses `SomeModule/SomeFeature/...` — match
|
|
5
|
-
whatever the existing slices use). The example slices use no file header (match the project); namespace
|
|
6
|
-
mirrors the folders and drops any `.Features.`. Examples below use short illustrative names
|
|
7
|
-
(`MyApp.Authors.Listing`); mirror your project's actual module/feature layout instead.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 1. Read model + queries (snapshot + observable)
|
|
12
|
-
|
|
13
|
-
```csharp
|
|
14
|
-
// Features/Authors/Listing/Listing.cs
|
|
15
|
-
namespace MyApp.Authors.Listing;
|
|
16
|
-
|
|
17
|
-
using System.Reactive.Subjects;
|
|
18
|
-
using Cratis.Arc.Queries.ModelBound;
|
|
19
|
-
using MongoDB.Driver;
|
|
20
|
-
|
|
21
|
-
[ReadModel]
|
|
22
|
-
public record AuthorListItem(AuthorId Id, AuthorName Name, int BookCount)
|
|
23
|
-
{
|
|
24
|
-
public static async Task<IEnumerable<AuthorListItem>> AllAuthors(
|
|
25
|
-
IMongoCollection<AuthorListItem> collection)
|
|
26
|
-
=> await collection.Find(Builders<AuthorListItem>.Filter.Empty).ToListAsync();
|
|
27
|
-
|
|
28
|
-
public static async Task<AuthorListItem?> GetAuthor(
|
|
29
|
-
AuthorId id, IMongoCollection<AuthorListItem> collection)
|
|
30
|
-
=> await collection.Find(a => a.Id == id).FirstOrDefaultAsync();
|
|
31
|
-
|
|
32
|
-
public static ISubject<IEnumerable<AuthorListItem>> ObserveAllAuthors(
|
|
33
|
-
IMongoCollection<AuthorListItem> collection)
|
|
34
|
-
=> collection.Observe();
|
|
35
|
-
|
|
36
|
-
public static ISubject<AuthorListItem> ObserveAuthor(
|
|
37
|
-
AuthorId id, IMongoCollection<AuthorListItem> collection)
|
|
38
|
-
=> collection.Observe(a => a.Id == id);
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Rules: `[ReadModel]` required; methods `public static`; observable methods return `ISubject<T>`
|
|
43
|
-
directly (never `Task<ISubject<T>>`); `ConceptAs<T>` for identity fields; one read model per use case.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## 2A. Projection (declarative mapping)
|
|
48
|
-
|
|
49
|
-
```csharp
|
|
50
|
-
public class AuthorListItemProjection : IProjectionFor<AuthorListItem>
|
|
51
|
-
{
|
|
52
|
-
public void Define(IProjectionBuilderFor<AuthorListItem> builder) => builder
|
|
53
|
-
.From<AuthorRegistered>(from => from
|
|
54
|
-
.Set(m => m.Name).To(e => e.Name)
|
|
55
|
-
.Set(m => m.BookCount).WithValue(0))
|
|
56
|
-
.From<BookAdded>(from => from
|
|
57
|
-
.Add(m => m.BookCount).With(_ => 1))
|
|
58
|
-
.From<BookRemoved>(from => from
|
|
59
|
-
.Subtract(m => m.BookCount).With(_ => 1));
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
- Keyed by **event source id** by default (the id passed to `IEventLog.Append`).
|
|
64
|
-
- AutoMap is on — `.From<E>()` directly; matching property names map automatically.
|
|
65
|
-
- Builder verbs: `Set(...).To(...)` / `.WithValue(...)`, `Add(...).With(...)`, `Subtract(...).With(...)`,
|
|
66
|
-
`.Join<T>()`, `.Children(...)`. Projections **join events, never read models**.
|
|
67
|
-
|
|
68
|
-
### Model-bound shorthand (simple cases)
|
|
69
|
-
```csharp
|
|
70
|
-
[ReadModel]
|
|
71
|
-
public record AuthorInfo(
|
|
72
|
-
[Key] Guid Id,
|
|
73
|
-
[FromEvent<AuthorRegistered>] string Name,
|
|
74
|
-
[SetFrom<AuthorRegistered>(nameof(AuthorRegistered.Country))] string Country);
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## 2B. Reducer (running aggregate)
|
|
80
|
-
|
|
81
|
-
```csharp
|
|
82
|
-
public class AccountBalanceReducer : IReducerFor<AccountBalance>
|
|
83
|
-
{
|
|
84
|
-
public AccountBalance Opened(DebitAccountOpened @event, AccountBalance? current, EventContext context)
|
|
85
|
-
=> new(0m, context.Occurred);
|
|
86
|
-
|
|
87
|
-
public AccountBalance Deposited(FundsDeposited @event, AccountBalance? current, EventContext context)
|
|
88
|
-
=> (current ?? new(0m, context.Occurred)) with { Balance = (current?.Balance ?? 0m) + @event.Amount };
|
|
89
|
-
|
|
90
|
-
public AccountBalance Withdrawn(FundsWithdrawn @event, AccountBalance? current, EventContext context)
|
|
91
|
-
=> (current ?? new(0m, context.Occurred)) with { Balance = (current?.Balance ?? 0m) - @event.Amount };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public record AccountBalance(decimal Balance, DateTimeOffset LastUpdated);
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Return the complete new state; never mutate `current` (`null` on the first event). Add
|
|
98
|
-
`[FilterEventsByTag]` / `[EventSourceType]` / `[EventStreamType]` when filtering by metadata.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## 3. Spec — projection result from a sequence of events
|
|
103
|
-
|
|
104
|
-
```csharp
|
|
105
|
-
namespace MyApp.Authors.Listing.for_AuthorListItem.when_an_author_registered_and_added_two_books;
|
|
106
|
-
|
|
107
|
-
// Use the project's projection-spec harness (e.g. a ProjectionSpecificationContext / Given<context>).
|
|
108
|
-
// The shape: append events, observe the resulting read model, assert fields.
|
|
109
|
-
|
|
110
|
-
public class and_projecting : Specification
|
|
111
|
-
{
|
|
112
|
-
AuthorListItem _result;
|
|
113
|
-
|
|
114
|
-
void Establish() { /* append AuthorRegistered, BookAdded, BookAdded to the test event store */ }
|
|
115
|
-
void Because() { /* run the projection / read the resulting model */ }
|
|
116
|
-
|
|
117
|
-
[Fact] void should_have_the_name() => _result.Name.Value.ShouldEqual("John");
|
|
118
|
-
[Fact] void should_count_two_books() => _result.BookCount.ShouldEqual(2);
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Match the project's existing projection-spec harness — discover it from an existing read slice's specs.
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## 4. React — render the query proxy
|
|
127
|
-
|
|
128
|
-
The query proxy (`AllAuthors.ts`) is generated **next to** `Listing.cs` — import it from the same
|
|
129
|
-
folder. The simplest list UI uses the Cratis `DataTableForObservableQuery` component (see the shipped
|
|
130
|
-
example slice's `ListingDataTable.tsx`):
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
// Authors/Listing/ListingDataTable.tsx
|
|
134
|
-
import { DataTableForObservableQuery } from '@cratis/components/DataTables';
|
|
135
|
-
import { Column } from 'primereact/column';
|
|
136
|
-
import { AllAuthors } from './AllAuthors'; // co-located generated proxy
|
|
137
|
-
|
|
138
|
-
export const ListingDataTable = () => (
|
|
139
|
-
<DataTableForObservableQuery query={AllAuthors} dataKey='id' emptyMessage='No authors yet.'>
|
|
140
|
-
<Column field='name' header='Name' />
|
|
141
|
-
<Column field='bookCount' header='Books' />
|
|
142
|
-
</DataTableForObservableQuery>
|
|
143
|
-
);
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Hook form (manual rendering):
|
|
147
|
-
|
|
148
|
-
```tsx
|
|
149
|
-
const [authors] = AllAuthors.use(); // snapshot or observable, depending on the proxy
|
|
150
|
-
const [result, , setPage] = AllAuthors.useWithPaging(10); // paging
|
|
151
|
-
// result.data, result.paging.totalItems, result.paging.page
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Observable queries generate an `ObservableQueryFor` proxy (from `collection.Observe()`); snapshot
|
|
155
|
-
queries generate `QueryFor`. PrimeReact CSS variables / Tailwind for styling; no `any`; full
|
|
156
|
-
descriptive names. Add a barrel `index.ts` for the slice.
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Checklist
|
|
161
|
-
|
|
162
|
-
- One `.cs` file, namespace without `.Features.` (no file header unless the project uses one).
|
|
163
|
-
- `[ReadModel]` record; `public static` query methods; observable returns `ISubject<T>` directly.
|
|
164
|
-
- Projection (mapping) or reducer (aggregate) — discovered automatically, no registration.
|
|
165
|
-
- Projections map from events only; one read model per use case.
|
|
166
|
-
- `ConceptAs<T>` for identity fields; one spec per scenario; `dotnet build` clean; specs pass.
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# CratisApp — agent conventions
|
|
2
|
-
|
|
3
|
-
This is a **Cratis** application: Cratis Arc (CQRS) + Cratis Chronicle (event sourcing) + MongoDB read
|
|
4
|
-
models on the backend, React + TypeScript (Vite + PrimeReact) on the frontend, with **full-stack type
|
|
5
|
-
safety** via TypeScript proxy generation on `dotnet build`.
|
|
6
|
-
|
|
7
|
-
Build slices with the kit's skills — `/build-state-change`, `/build-state-view`, `/build-automation` —
|
|
8
|
-
and follow the conventions distilled in
|
|
9
|
-
`.build-kit/.claude/skills/_shared/cratis-conventions.md`. **The shipped example slice
|
|
10
|
-
under `SomeModule/SomeFeature/` is the concrete pattern to copy** — match its structure exactly.
|
|
11
|
-
|
|
12
|
-
**If a slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need in
|
|
13
|
-
order to proceed — do not guess, and do not build anyway.** Invoke the `request-feedback` skill with
|
|
14
|
-
the specific question; it posts the question as a comment on the slice and marks it `Blocked`, and you
|
|
15
|
-
then stop work on this slice for this run. This is an escalation path, not a routine step — read
|
|
16
|
-
`slice.json` and the matching build skill's own instructions fully first; most slices are fully
|
|
17
|
-
specified and need none of this.
|
|
18
|
-
|
|
19
|
-
## No such thing as harmless drift
|
|
20
|
-
|
|
21
|
-
`slice.json` is always the desired state — the code follows what it defines, never the other way
|
|
22
|
-
around. If code for a slice already exists — most often because it was previously `Done` and got moved
|
|
23
|
-
back to `Planned` — never conclude "already implemented" and move on. A slice does not return to
|
|
24
|
-
`Planned` without a reason: diff the current `slice.json` against the existing implementation field by
|
|
25
|
-
field (events, commands, params, specifications, routes) and update the code to match every difference
|
|
26
|
-
you find. Only mark it `Done` once there is no gap left.
|
|
27
|
-
|
|
28
|
-
## Structure (learn from `SomeModule/SomeFeature/`)
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
<Module>/<Feature>/
|
|
32
|
-
├── <Feature>.tsx ← composition page (buttons, dialogs, tables)
|
|
33
|
-
├── index.ts ← barrel: export * from './<Feature>'
|
|
34
|
-
├── <Concept>.cs ← shared concepts (ConceptAs<T>)
|
|
35
|
-
└── <Slice>/ ← one slice = one behavior
|
|
36
|
-
├── <Slice>.cs ← ALL backend artifacts for the slice in ONE file
|
|
37
|
-
├── <Proxy>.ts ← GENERATED by dotnet build — never hand-edit
|
|
38
|
-
├── <Component>.tsx ← React component using the generated proxy
|
|
39
|
-
└── index.ts ← barrel export
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
- Top folder is a **module**, then a **feature**, then **slices**. Namespace is
|
|
43
|
-
`CratisApp.<Module>.<Feature>.<Slice>` (the `RootNamespace` is `CratisApp` — see `CratisApp.csproj`).
|
|
44
|
-
- **Proxies are generated next to the source** (`CratisProxiesUseSourceFileAsOutputFile=true` in the
|
|
45
|
-
`.csproj`): `Register.ts` sits beside `Registration.cs`. Frontend imports the proxy from the **same
|
|
46
|
-
folder** (`import { Register } from './Register'`). Files marked `// @generated` are never edited.
|
|
47
|
-
|
|
48
|
-
## Non-negotiables (full detail in the shared conventions doc)
|
|
49
|
-
|
|
50
|
-
- ALL backend artifacts for a slice live in ONE `.cs` file.
|
|
51
|
-
- `[Command]` records define `Handle()` directly — never separate handler classes.
|
|
52
|
-
- `[EventType]` takes NO arguments; events are past-tense and never nullable.
|
|
53
|
-
- `ConceptAs<T>` for identity/value types — no raw `Guid` / `string` in the domain.
|
|
54
|
-
- `[ReadModel]` records expose queries as `public static` methods; observable queries return
|
|
55
|
-
`ISubject<T>` directly (`collection.Observe()`).
|
|
56
|
-
- Reactors implement the marker `IReactor`; dispatch is by the first parameter type. Write new events
|
|
57
|
-
only via `ICommandPipeline.Execute(...)`, never `IEventLog`. Keep reactors idempotent + stateless.
|
|
58
|
-
- Frontend: `CommandDialog` / `InputTextField` from `@cratis/components/*`; never import `Dialog` from
|
|
59
|
-
`primereact/dialog`. PrimeReact CSS variables / Tailwind classes for styling; no `any`.
|
|
60
|
-
|
|
61
|
-
## Build, run, test
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
docker-compose up -d # Chronicle (+ MongoDB) + Aspire dashboard
|
|
65
|
-
dotnet build # compiles backend AND regenerates TypeScript proxies
|
|
66
|
-
dotnet test # run specs (filter while iterating: --filter "FullyQualifiedName~<Slice>")
|
|
67
|
-
npm install && npm run dev # frontend dev server (Vite)
|
|
68
|
-
dotnet run # backend (http://localhost:5000, Swagger at /swagger)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**Sequencing is strict:** a slice's frontend cannot reference its proxy until the backend compiles, so
|
|
72
|
-
always go Backend → `dotnet build` → Specs → Frontend → Composition page → Routes (`App.tsx`). Backend
|
|
73
|
-
and frontend of the same slice never run in parallel. Build must be zero-warning, zero-error.
|
|
74
|
-
|
|
75
|
-
## Learn more
|
|
76
|
-
|
|
77
|
-
- Cratis docs: https://www.cratis.io/docs/
|
|
78
|
-
- Conventions: `.build-kit/.claude/skills/_shared/cratis-conventions.md`
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# Agent Learnings
|
|
2
|
-
|
|
3
|
-
Patterns and gotchas discovered during task processing. Update this file whenever you encounter something reusable.
|
|
4
|
-
|
|
5
|
-
## Cratis non-negotiables (seed — full detail in `.claude/skills/_shared/cratis-conventions.md`)
|
|
6
|
-
|
|
7
|
-
- ALL backend artifacts for a slice go in ONE `.cs` file under the project's slice folder (the shipped starter uses `<Module>/<Feature>/<Slice>/<Slice>.cs` — discover the real top-level folder from an existing slice). Never split into `Commands/`, `Handlers/`, `Events/`.
|
|
8
|
-
- `[Command]` records define `Handle()` directly on the record — never a separate handler class.
|
|
9
|
-
- `[EventType]` takes NO attribute arguments (the type name is the id); events are past-tense and never nullable.
|
|
10
|
-
- Use `ConceptAs<T>` for every identity/value — no raw `Guid`/`string` in the domain.
|
|
11
|
-
- Read models: `[ReadModel]` record with `public static` query methods on it; observable queries return `ISubject<T>` directly (never `Task<ISubject<T>>`). Projections join events, never read models.
|
|
12
|
-
- Reactors implement the marker `IReactor`; dispatch is by the first parameter type. Write new events only via `ICommandPipeline.Execute(...)`, never `IEventLog`. Reactors must be idempotent and stateless.
|
|
13
|
-
- Namespace mirrors the folders and drops any `.Features.` segment: `<Root>.<Module>.<Feature>.<Slice>` (the starter's `<Root>` is `CratisApp`). Find `<Root>` from the `.csproj` `<RootNamespace>` / existing slices; never hard-code.
|
|
14
|
-
- `dotnet build` generates the TypeScript proxies — backend must compile before a slice's frontend can reference them. Order: Backend → build → Specs → Frontend → Composition.
|
|
15
|
-
- Quality gate: `dotnet build` with zero warnings/errors (warnings = errors); `dotnet test --filter "FullyQualifiedName~<SliceName>"`. File header only if the project's existing `.cs` files already carry one (the shipped example uses none).
|
|
16
|
-
|
|
17
|
-
## tasks.json
|
|
18
|
-
|
|
19
|
-
- Tasks are objects with `id`, `createdAt`, and `payload` (a `SliceChangedPayload`).
|
|
20
|
-
- After completing a task, remove it from the array entirely — do not add a status field.
|
|
21
|
-
- Write `[]` to `tasks.json` if the last task is completed.
|
|
22
|
-
|
|
23
|
-
## SliceChangedPayload fields
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
event always "slice:changed"
|
|
27
|
-
organizationId org UUID or null
|
|
28
|
-
boardId board UUID
|
|
29
|
-
sliceId SLICE_BORDER node UUID — use this with /load-slice
|
|
30
|
-
sliceTitle human-readable slice name (may be null)
|
|
31
|
-
sliceStatus e.g. "Created", "InProgress", "Done", "Blocked" (may be null)
|
|
32
|
-
timestamp unix ms when the change was emitted
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Slice files
|
|
36
|
-
|
|
37
|
-
The realtime agent writes one file per slice on startup and after each `slice:changed` event:
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
.slices/<context>/<sliceName>/slice.json
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
- `<context>` is the slice's context value, or `default` if none.
|
|
44
|
-
- `<sliceName>` is the slice title lowercased with spaces removed (e.g. `"Enable User"` → `enableuser`).
|
|
45
|
-
|
|
46
|
-
These files are always up to date — read them directly before invoking any skill.
|
|
47
|
-
|
|
48
|
-
## Skill Usage
|
|
49
|
-
|
|
50
|
-
- Always run `/connect` first to load credentials from `.eventmodelers/config.json` before calling any other skill.
|
|
51
|
-
- `/load-slice sliceId=<uuid>` re-fetches all slices from the API, refreshes the slice files, and returns the requested slice. Use it when you need a guaranteed-fresh view of a specific slice.
|
|
52
|
-
- Read `.slices/<context>/<sliceName>/slice.json` directly when you already know the context and name and the file is recent enough.
|
|
53
|
-
|
|
54
|
-
## Board API
|
|
55
|
-
|
|
56
|
-
- The `boardId` and `organizationId` from each payload provide full context — pass them to skills.
|
|
57
|
-
- Node events use `node:created`, `node:changed`, `node:deleted` — always POST to `/api/org/:orgId/boards/:boardId/nodes/events`.
|
|
58
|
-
- Slice metadata (title, status) lives on the SLICE_BORDER node under `meta.sliceStatus` and `meta.title`.
|
|
59
|
-
- `/update-slice-status` rejects moving a slice into a status it's already in — this is a concurrency guard, not a bug. It means another agent already claimed the slice. Treat it as `ALREADY_IN_STATUS`, skip that slice, and move on to the next `Planned` one instead of erroring out.
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
# Ralph Agent Instructions — Cratis (Arc + Chronicle)
|
|
2
|
-
|
|
3
|
-
You are an autonomous coding agent building software slices in a **Cratis** .NET / C# project. You
|
|
4
|
-
apply your skills to implement one slice at a time, the Cratis way.
|
|
5
|
-
|
|
6
|
-
The conventions in `.claude/skills/_shared/cratis-conventions.md` are authoritative — read them before
|
|
7
|
-
implementing.
|
|
8
|
-
|
|
9
|
-
## Your Task
|
|
10
|
-
|
|
11
|
-
0. Do not read the entire codebase. Focus on the task in this description.
|
|
12
|
-
1. Read `.build-kit/.slices/current_context.json` to find the active context name, then
|
|
13
|
-
read `.build-kit/.slices/<contextName>/index.json`. Every item with status "Planned"
|
|
14
|
-
is a task.
|
|
15
|
-
2. Read the progress log at `progress.txt` (check the **Codebase Patterns** section first) and the
|
|
16
|
-
accumulated learnings in `AGENT.md`.
|
|
17
|
-
3. Make sure you are on the right branch `feature/<slicename>`; if unsure, start from `main`.
|
|
18
|
-
4. Pick the **highest priority** slice where status is **exactly** "Planned" (case insensitive). This
|
|
19
|
-
becomes your PRD. Set its status to "InProgress" in `index.json` **and** on the board via the
|
|
20
|
-
`update-slice-status` skill (or MCP if available).
|
|
21
|
-
**Only work on slices with status "Planned". Never pick up "InProgress", "Done", "Blocked",
|
|
22
|
-
"Created", or any other status — even if it looks incomplete. If no slice is "Planned", reply with**
|
|
23
|
-
`<promise>NO_TASKS</promise>` **and stop immediately.**
|
|
24
|
-
5. Read the slice definition from
|
|
25
|
-
`.build-kit/.slices/<contextName>/<folder>/slice.json` — it is the source of truth.
|
|
26
|
-
Never work on more than one slice per iteration.
|
|
27
|
-
6. A slice may define additional `codegen` / `backendPrompt` hints — take them into account and note in
|
|
28
|
-
`progress.txt` when used.
|
|
29
|
-
7. Determine the slice type and load the matching skill:
|
|
30
|
-
- **State Change** (has `commands` / `events`, no `processors`) → `/build-state-change`
|
|
31
|
-
- **State View** (has `readModel` / `projections` / `queries`) → `/build-state-view`
|
|
32
|
-
- **Automation** (`processors` array non-empty) → `/build-automation`
|
|
33
|
-
- **Translation** (`sliceType === "TRANSLATION"`) → read `description` / `notes` for hints; default
|
|
34
|
-
to `/build-automation` (a reactor that triggers a command in its own slice)
|
|
35
|
-
8. Write a short one-line progress note to `progress.txt` after each step.
|
|
36
|
-
9. Implement that single slice using the matching skill as guidance. Make a TODO list of what's needed.
|
|
37
|
-
The JSON is the desired state — carefully compare events, fields, commands, and specifications
|
|
38
|
-
against the code. A "Planned" task may be **added specifications** on an existing slice: always look
|
|
39
|
-
at the slice AND its specifications, and add any missing specs in code.
|
|
40
|
-
10. The JSON is always true — the code follows what the JSON defines.
|
|
41
|
-
11. A slice is only "Done" when the business logic is implemented as defined, the read/write/automation
|
|
42
|
-
artifacts exist, every specification in the JSON has an executable equivalent in code, and it
|
|
43
|
-
fulfills `slice.json`. There must be no specification in the JSON without a code equivalent.
|
|
44
|
-
12. **Follow the Cratis non-negotiables** (full detail in `cratis-conventions.md`):
|
|
45
|
-
- ALL backend artifacts for the slice in ONE `.cs` file under the project's slice folder
|
|
46
|
-
(the shipped starter uses `<Module>/<Feature>/<Slice>/<Slice>.cs` — discover the real
|
|
47
|
-
top-level folder from an existing slice and match it).
|
|
48
|
-
- `[Command]` records with `Handle()` on the record — never separate handler classes.
|
|
49
|
-
- `[EventType]` with NO attribute arguments; past-tense names; no nullable properties.
|
|
50
|
-
- `ConceptAs<T>` for every identity / value — no raw `Guid` / `string` in the domain.
|
|
51
|
-
- Namespace mirrors the folders and drops any `.Features.` segment
|
|
52
|
-
(`<Root>.<Module>.<Feature>.<Slice>`; the starter's `<Root>` is `CratisApp`). Read the
|
|
53
|
-
`<RootNamespace>` in the `.csproj` and existing slices to find the root; never hard-code it.
|
|
54
|
-
- File header: only if the project's existing `.cs` files already carry one — the shipped
|
|
55
|
-
example slices use none, so default to no header.
|
|
56
|
-
13. Run quality checks — it is enough to run the tests for the slice only, not all tests. Run from the
|
|
57
|
-
**project root** (where the `.csproj`/solution lives), not the kit folder:
|
|
58
|
-
- Build: `dotnet build` (zero warnings, zero errors — warnings are treated as errors; this also
|
|
59
|
-
regenerates the TypeScript proxies)
|
|
60
|
-
- Test (slice only): `dotnet test --filter "FullyQualifiedName~<SliceName>"`
|
|
61
|
-
If the slice is UI-triggered, also implement the React component(s) AFTER the build generated the
|
|
62
|
-
proxies, then register them in the feature's composition page.
|
|
63
|
-
14. If checks pass, commit ALL changes with message `feat: <Slice Name>` and merge back to `main` as a
|
|
64
|
-
fast-forward merge (update first).
|
|
65
|
-
15. Set `status: Done` for the slice in `index.json` **and** on the board via `update-slice-status`.
|
|
66
|
-
16. Append progress to `progress.txt` after each step.
|
|
67
|
-
17. Append new reusable learnings to `AGENT.md` in compressed form (only if not already there).
|
|
68
|
-
18. Finish the iteration.
|
|
69
|
-
|
|
70
|
-
## Sequencing (Cratis-specific)
|
|
71
|
-
|
|
72
|
-
`dotnet build` generates the TypeScript proxies — **the frontend of a slice cannot reference its proxy
|
|
73
|
-
until the backend compiles**. Always: Backend → `dotnet build` → Specs → Frontend → Composition.
|
|
74
|
-
Never implement a slice's frontend before its backend builds.
|
|
75
|
-
|
|
76
|
-
## Escalating Ambiguity
|
|
77
|
-
|
|
78
|
-
**If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need
|
|
79
|
-
in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the
|
|
80
|
-
specific question; it posts the question as a comment on the slice and marks it `Blocked` on the
|
|
81
|
-
board (overriding the `InProgress` set earlier), then stop this iteration without finishing the
|
|
82
|
-
build — reply `<promise>DONE</promise>` as if the iteration's work was to raise the question, not to
|
|
83
|
-
implement the slice. This is an escalation path, not a routine step — read the slice.json and the
|
|
84
|
-
matching build skill's own instructions fully first; most slices are fully specified and need none of
|
|
85
|
-
this.
|
|
86
|
-
|
|
87
|
-
## Progress Report Format
|
|
88
|
-
|
|
89
|
-
APPEND to `progress.txt` (never replace):
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
## [Date/Time] - [Slice]
|
|
93
|
-
|
|
94
|
-
- What was implemented
|
|
95
|
-
- Files changed
|
|
96
|
-
- **Learnings for future iterations:**
|
|
97
|
-
- Patterns discovered (e.g., "this project's namespace root is `Library`")
|
|
98
|
-
- Gotchas (e.g., "observable queries must return ISubject<T>, never Task<ISubject<T>>")
|
|
99
|
-
- Useful context
|
|
100
|
-
---
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Consolidate Patterns
|
|
104
|
-
|
|
105
|
-
If you discover a **reusable** pattern, add it to a `## Codebase Patterns` section at the TOP of
|
|
106
|
-
`progress.txt` (create it if missing). Only general, reusable patterns — not slice-specific details.
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
## Codebase Patterns
|
|
110
|
-
- Namespace root is `<Root>`; slices live under <Module>/<Feature>/<Slice>/
|
|
111
|
-
- Read models expose queries as public static methods on the [ReadModel] record
|
|
112
|
-
- DCB rules: inject the read model as a Handle() parameter
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Quality Requirements
|
|
116
|
-
|
|
117
|
-
- ALL commits must pass `dotnet build` (zero warnings/errors) and the slice's `dotnet test`.
|
|
118
|
-
- Do NOT commit broken code. Keep changes focused and minimal. Follow existing patterns.
|
|
119
|
-
|
|
120
|
-
## Specifications
|
|
121
|
-
|
|
122
|
-
For every specification on the slice, implement one executable spec in code (Cratis.Specifications BDD:
|
|
123
|
-
`Establish` / `Because` / `[Fact] should_*`). A slice is not complete if specifications are missing or
|
|
124
|
-
cannot execute.
|
|
125
|
-
|
|
126
|
-
## Stop Condition
|
|
127
|
-
|
|
128
|
-
**After completing ONE slice, always stop** — the ralph loop will invoke you again for the next slice.
|
|
129
|
-
Never chain multiple slices in one iteration.
|
|
130
|
-
|
|
131
|
-
- Slice completed and committed → reply `<promise>DONE</promise>`
|
|
132
|
-
- No slice has status "Planned" → reply `<promise>NO_TASKS</promise>`
|
|
133
|
-
- ALL slices across the index are Done → reply `<promise>COMPLETE</promise>`
|
|
134
|
-
|
|
135
|
-
## Important
|
|
136
|
-
|
|
137
|
-
- If `.build-kit/.eventmodelers/config.json` is absent, skip all platform communication
|
|
138
|
-
(MCP calls, `update-slice-status`, board sync) and continue working locally.
|
|
139
|
-
- Work on ONE slice per iteration. Commit frequently. Update `progress.txt` frequently.
|
|
140
|
-
- Read the Codebase Patterns section in `progress.txt` and `AGENT.md` before starting.
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
# Agent Task Instructions
|
|
2
|
-
|
|
3
|
-
You are an autonomous agent reacting to slice status change events on an Eventmodelers board.
|
|
4
|
-
|
|
5
|
-
## Your Loop
|
|
6
|
-
|
|
7
|
-
1. Read `AGENT.md` to load accumulated learnings before doing anything else.
|
|
8
|
-
2. Read `.build-kit/tasks.json`.
|
|
9
|
-
3. If `tasks.json` is empty or missing, reply with:
|
|
10
|
-
<promise>IDLE</promise>
|
|
11
|
-
and stop.
|
|
12
|
-
4. Pick the **oldest task** (earliest `createdAt`).
|
|
13
|
-
5. Execute the task — see the Execution section below.
|
|
14
|
-
6. After execution, remove that task from the array and write `.build-kit/tasks.json` back.
|
|
15
|
-
7. Append a progress entry to `progress.txt` (create if missing).
|
|
16
|
-
8. Update `AGENT.md` with any new reusable learnings discovered this iteration.
|
|
17
|
-
9. Reply normally so the next iteration can pick up the next task.
|
|
18
|
-
|
|
19
|
-
## Execution
|
|
20
|
-
|
|
21
|
-
Each task has a single `payload` of type `SliceChangedPayload`:
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
{
|
|
25
|
-
event: "slice:changed"
|
|
26
|
-
organizationId: string | null
|
|
27
|
-
boardId: string
|
|
28
|
-
sliceId: string ← SLICE_BORDER node UUID
|
|
29
|
-
sliceTitle: string | null
|
|
30
|
-
sliceStatus: string | null ← e.g. "InProgress", "Done", "Blocked"
|
|
31
|
-
timestamp: number
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Step 1 — Load credentials
|
|
36
|
-
|
|
37
|
-
Run `/connect` to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL` from `.eventmodelers/config.json`.
|
|
38
|
-
|
|
39
|
-
### Step 2 — Load the slice
|
|
40
|
-
|
|
41
|
-
Run `/load-slice sliceId=<payload.sliceId>` to fetch full slice details (title, status, raw node record).
|
|
42
|
-
|
|
43
|
-
### Step 3 — Act on the change
|
|
44
|
-
|
|
45
|
-
Inspect the `sliceStatus` in the payload:
|
|
46
|
-
|
|
47
|
-
#### `Planned` — build the slice
|
|
48
|
-
|
|
49
|
-
This is the build trigger. Setting `InProgress` and building are one atomic step:
|
|
50
|
-
|
|
51
|
-
**If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked` (superseding the `InProgress` set in step 1), then drop this task without finishing the build. This is an escalation path, not a routine step — most slices are fully specified.
|
|
52
|
-
|
|
53
|
-
1. Immediately call `/update-slice-status` to set the slice to `InProgress` on the board.
|
|
54
|
-
|
|
55
|
-
2. Read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json` (written by `/load-slice`).
|
|
56
|
-
|
|
57
|
-
3. Determine the **slice type** from the slice.json:
|
|
58
|
-
- **Translation** — `sliceType === "TRANSLATION"` → read `description` and `notes` from slice.json for hints; default to `/build-automation` if nothing else is specified
|
|
59
|
-
- **Automation** — `processors` array is non-empty → invoke `/build-automation`
|
|
60
|
-
- **State-view** — `projections` or `queries` array is non-empty → invoke `/build-state-view`
|
|
61
|
-
- **State-change** — default (has `commands` / `events`) → invoke `/build-state-change`
|
|
62
|
-
|
|
63
|
-
4. Invoke the matching skill and follow its instructions **completely**. Do NOT implement the slice manually.
|
|
64
|
-
|
|
65
|
-
5. **Verify against slice.json**: Cross-check the implementation — every command field, event field, and specification in slice.json must appear in the code. No invented fields — if it is not in slice.json, it must not be in the code.
|
|
66
|
-
|
|
67
|
-
6. Run quality checks (from the project root — `dotnet build` also regenerates the TypeScript proxies the frontend depends on):
|
|
68
|
-
- Build: `dotnet build` (zero warnings, zero errors)
|
|
69
|
-
- Test (slice only): `dotnet test --filter "FullyQualifiedName~<SliceName>"`
|
|
70
|
-
|
|
71
|
-
7. If checks pass, commit all changes with message: `feat: [Slice Name]`.
|
|
72
|
-
|
|
73
|
-
8. Call `/update-slice-status` to set the slice to `Done` on the board.
|
|
74
|
-
|
|
75
|
-
#### `InProgress`
|
|
76
|
-
Another agent is already building this slice. Log it and skip — do not build.
|
|
77
|
-
|
|
78
|
-
#### `Done`
|
|
79
|
-
Summarize what was completed and update `progress.txt`.
|
|
80
|
-
|
|
81
|
-
#### `Blocked`
|
|
82
|
-
Log the blocker in `progress.txt`.
|
|
83
|
-
|
|
84
|
-
#### `Review`
|
|
85
|
-
Fetch slice details and prepare a review summary in `progress.txt`.
|
|
86
|
-
|
|
87
|
-
#### Any other status (`Created`, etc.)
|
|
88
|
-
Load the slice and log the state transition in `progress.txt`. No build action.
|
|
89
|
-
|
|
90
|
-
Use the skills available in `.claude/skills/` to interact with the board.
|
|
91
|
-
|
|
92
|
-
## Updating tasks.json
|
|
93
|
-
|
|
94
|
-
After completing a task, remove it from the array and write the updated array back to `.build-kit/tasks.json`. If the array is now empty, write `[]`.
|
|
95
|
-
|
|
96
|
-
## Progress Report Format
|
|
97
|
-
|
|
98
|
-
APPEND to `progress.txt` (never replace):
|
|
99
|
-
```
|
|
100
|
-
## [ISO timestamp] — Task [task.id]
|
|
101
|
-
|
|
102
|
-
Slice: [sliceTitle] ([sliceId])
|
|
103
|
-
Status change: [sliceStatus]
|
|
104
|
-
|
|
105
|
-
Action taken:
|
|
106
|
-
- [what was done in response to the slice change]
|
|
107
|
-
|
|
108
|
-
Learnings:
|
|
109
|
-
- [any patterns, gotchas, or reusable knowledge discovered]
|
|
110
|
-
---
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Stop Condition
|
|
114
|
-
|
|
115
|
-
If `.build-kit/tasks.json` is empty (`[]`) or does not exist, reply with:
|
|
116
|
-
<promise>IDLE</promise>
|
|
117
|
-
|
|
118
|
-
## Updating AGENT.md
|
|
119
|
-
|
|
120
|
-
After completing a task, add any **reusable** learnings to `AGENT.md` — patterns, gotchas, API quirks, or skill behaviour that future iterations should know. Only add things that are general and applicable beyond this single task. Do not duplicate what is already there.
|
|
121
|
-
|
|
122
|
-
## Important
|
|
123
|
-
|
|
124
|
-
- Process **one task per iteration**.
|
|
125
|
-
- Read `AGENT.md` first — it contains patterns from previous iterations.
|
|
126
|
-
- Always start with `/connect` if credentials are not yet loaded.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
@import "tailwindcss";
|
|
2
|
-
@import 'primereact/resources/themes/lara-dark-blue/theme.css';
|
|
3
|
-
@source '../';
|
|
4
|
-
|
|
5
|
-
:root {
|
|
6
|
-
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
|
7
|
-
font-size: 14px;
|
|
8
|
-
line-height: 1.5;
|
|
9
|
-
font-weight: 400;
|
|
10
|
-
|
|
11
|
-
color-scheme: dark;
|
|
12
|
-
|
|
13
|
-
font-synthesis: none;
|
|
14
|
-
text-rendering: optimizeLegibility;
|
|
15
|
-
-webkit-font-smoothing: antialiased;
|
|
16
|
-
-moz-osx-font-smoothing: grayscale;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
body {
|
|
20
|
-
margin: 0;
|
|
21
|
-
min-width: 320px;
|
|
22
|
-
min-height: 100vh;
|
|
23
|
-
background-color: var(--surface-ground);
|
|
24
|
-
color: var(--text-color);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
#root {
|
|
28
|
-
height: 100vh;
|
|
29
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8" />
|
|
6
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
-
<meta name="base-path" content="" />
|
|
9
|
-
<title>CratisApp</title>
|
|
10
|
-
</head>
|
|
11
|
-
|
|
12
|
-
<body>
|
|
13
|
-
<div id="root"></div>
|
|
14
|
-
<script type="module" src="/main.tsx"></script>
|
|
15
|
-
</body>
|
|
16
|
-
|
|
17
|
-
</html>
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
|
-
import { PrimeReactProvider } from 'primereact/api';
|
|
3
|
-
import ReactDOM from 'react-dom/client';
|
|
4
|
-
import 'primeicons/primeicons.css';
|
|
5
|
-
import './index.css';
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { Arc } from '@cratis/arc.react';
|
|
8
|
-
import App from '../App.tsx';
|
|
9
|
-
|
|
10
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
11
|
-
<React.StrictMode>
|
|
12
|
-
<PrimeReactProvider value={{ ripple: true }}>
|
|
13
|
-
<Arc>
|
|
14
|
-
<App />
|
|
15
|
-
</Arc>
|
|
16
|
-
</PrimeReactProvider>
|
|
17
|
-
</React.StrictMode>
|
|
18
|
-
);
|