@nebulit/embuilder 0.1.47 → 0.1.49

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebulit/embuilder",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "description": "Event-model driven development toolkit for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -104,20 +104,22 @@ import { ContextEvents } from '../../events/ContextEvents';
104
104
  - Use proper `initialState` function (not empty object `{}`) in `DeciderSpecification.for()`
105
105
  - Do NOT add explicit type arguments - let TypeScript infer (avoids TS2558)
106
106
  - Switch statements: always use explicit `break` to prevent fallthrough bugs
107
+ - you must not change the signature of evolve - it has (state:State, event:Event)
108
+ - you must not change the signature of decide - it has (state:State, command:Command) - all data to verify the command must be passed via state.
107
109
 
108
110
  **Structure**:
109
111
  ```typescript
110
112
  type State = { /* track relevant state */ };
111
113
  const initialState = (): State => ({ /* defaults */ });
112
114
  const decide = (state: State, cmd: Command): Event[] => {
113
- if (invalidCondition) throw 'error.code';
114
- return [createEvent(cmd)];
115
+ if (invalidCondition) throw 'error.code';
116
+ return [createEvent(cmd)];
115
117
  };
116
118
  const evolve = (state: State, event: ContextEvents): State => {
117
- switch (event.type) {
118
- case 'EventType': return { ...state, field: newValue };
119
- default: return state;
120
- }
119
+ switch (event.type) {
120
+ case 'EventType': return { ...state, field: newValue };
121
+ default: return state;
122
+ }
121
123
  };
122
124
  ```
123
125
 
@@ -133,14 +135,14 @@ const evolve = (state: State, event: ContextEvents): State => {
133
135
  **Projection Structure**:
134
136
  ```typescript
135
137
  export const ProjectionName = postgreSQLRawSQLProjection({
136
- canHandle: ['Event1', 'Event2'],
137
- evolve: (event: Event1 | Event2) => {
138
- switch (event.type) {
139
- case 'Event1': return sql(db(table).insert({...}).onConflict().merge());
140
- case 'Event2': return sql(db(table).where({...}).delete());
141
- default: return [];
138
+ canHandle: ['Event1', 'Event2'],
139
+ evolve: (event: Event1 | Event2) => {
140
+ switch (event.type) {
141
+ case 'Event1': return sql(db(table).insert({...}).onConflict().merge());
142
+ case 'Event2': return sql(db(table).where({...}).delete());
143
+ default: return [];
144
+ }
142
145
  }
143
- }
144
146
  });
145
147
  ```
146
148
 
@@ -158,22 +160,22 @@ import { createServiceClient } from '../../common/supabaseClient';
158
160
  const config = { schedule: '*/30 * * * * *', endpoint: "work_queue_table" };
159
161
 
160
162
  export const processor = {
161
- start: () => {
162
- cron.schedule(config.schedule, async () => {
163
- const client = createServiceClient();
164
- const result = await client.from(config.endpoint)
165
- .select("*").eq('must_process', true).limit(1);
166
-
167
- if (result.error) return;
168
- for (const item of result.data ?? []) {
169
- try {
170
- await handleCommand(streamId, command, { userId: 'system', ...metadata });
171
- } catch (error) {
172
- console.error('Processing error:', error);
173
- }
174
- }
175
- });
176
- }
163
+ start: () => {
164
+ cron.schedule(config.schedule, async () => {
165
+ const client = createServiceClient();
166
+ const result = await client.from(config.endpoint)
167
+ .select("*").eq('must_process', true).limit(1);
168
+
169
+ if (result.error) return;
170
+ for (const item of result.data ?? []) {
171
+ try {
172
+ await handleCommand(streamId, command, { userId: 'system', ...metadata });
173
+ } catch (error) {
174
+ console.error('Processing error:', error);
175
+ }
176
+ }
177
+ });
178
+ }
177
179
  };
178
180
  ```
179
181
 
@@ -189,9 +191,9 @@ export const processor = {
189
191
  ```typescript
190
192
  type State = { isActive: boolean };
191
193
  const decide = (state: State, cmd: Command): Event[] => {
192
- if (state.isActive) throw "already.active"; // Activate guard
193
- if (!state.isActive) throw "not.active"; // Deactivate guard
194
- return [event];
194
+ if (state.isActive) throw "already.active"; // Activate guard
195
+ if (!state.isActive) throw "not.active"; // Deactivate guard
196
+ return [event];
195
197
  };
196
198
  ```
197
199
 
@@ -201,13 +203,13 @@ const decide = (state: State, cmd: Command): Event[] => {
201
203
  ```typescript
202
204
  type State = { trackedIds: Set<string> };
203
205
  const evolve = (state: State, event: ContextEvents): State => {
204
- switch (event.type) {
205
- case 'ItemAdded': return { trackedIds: new Set([...state.trackedIds, event.data.id]) };
206
- case 'ItemRemoved':
207
- const newSet = new Set(state.trackedIds);
208
- newSet.delete(event.data.id);
209
- return { trackedIds: newSet };
210
- }
206
+ switch (event.type) {
207
+ case 'ItemAdded': return { trackedIds: new Set([...state.trackedIds, event.data.id]) };
208
+ case 'ItemRemoved':
209
+ const newSet = new Set(state.trackedIds);
210
+ newSet.delete(event.data.id);
211
+ return { trackedIds: newSet };
212
+ }
211
213
  };
212
214
  ```
213
215
 
@@ -217,10 +219,10 @@ const evolve = (state: State, event: ContextEvents): State => {
217
219
  ```typescript
218
220
  type State = { submitted: boolean; reverted: boolean; approved: boolean; declined: boolean };
219
221
  const decide = (state: State, cmd: Command): Event[] => {
220
- if (state.submitted) throw 'cannot submit twice';
221
- if (!state.submitted) throw 'not_submitted';
222
- if (state.reverted) throw 'already_reverted';
223
- return [event];
222
+ if (state.submitted) throw 'cannot submit twice';
223
+ if (!state.submitted) throw 'not_submitted';
224
+ if (state.reverted) throw 'already_reverted';
225
+ return [event];
224
226
  };
225
227
  ```
226
228
 
@@ -275,27 +277,27 @@ All endpoints must:
275
277
  ### STATE_CHANGE Tests
276
278
  ```typescript
277
279
  DeciderSpecification.for(decide, evolve, initialState)
278
- .given([PrerequisiteEvent1, PrerequisiteEvent2])
279
- .when(Command({ field: 'value' }))
280
- .then([ExpectedEvent({ field: 'value' })]);
280
+ .given([PrerequisiteEvent1, PrerequisiteEvent2])
281
+ .when(Command({ field: 'value' }))
282
+ .then([ExpectedEvent({ field: 'value' })]);
281
283
  ```
282
284
 
283
285
  ### STATE_VIEW Tests
284
286
  ```typescript
285
287
  PostgreSQLProjectionSpec.for(ProjectionName)
286
- .given([Event1, Event2])
287
- .when([]) // Always empty
288
- .then(async (state) => {
289
- const result = await state.query();
290
- assert.equal(result[0].field, expectedValue);
291
- });
288
+ .given([Event1, Event2])
289
+ .when([]) // Always empty
290
+ .then(async (state) => {
291
+ const result = await state.query();
292
+ assert.equal(result[0].field, expectedValue);
293
+ });
292
294
  ```
293
295
 
294
296
  ### Error Case Tests
295
297
  ```typescript
296
298
  .given([EventCreated])
297
- .when(Command)
298
- .shouldFail(); // Expects error
299
+ .when(Command)
300
+ .shouldFail(); // Expects error
299
301
  ```
300
302
 
301
303
  ### Test Coverage
@@ -3,6 +3,8 @@
3
3
  You are an autonomous coding agent working on the backend of a software project. You apply your skills to build software slices. You only work on one slice at a time.
4
4
 
5
5
  The structure defined in the Project-Skills is relevant.
6
+ If a slice is in status 'planned', Even if a slice seems to have been implemented, make sure to verify the implementation, check for new fields, new specifications, additional custom prompts in the slice json. A "planned" slice can also be an update of an existing slice. If that is the case, match the implemenetation to the updated slice definition.
7
+
6
8
 
7
9
  ## Your Task
8
10
 
@@ -10,27 +12,26 @@ The structure defined in the Project-Skills is relevant.
10
12
  1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" and assigned to "backend_worker" is a task.
11
13
  2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
12
14
  3. Make sure you are on the right branch "feature/<slicename>", if unsure, start from main.
13
- 5. Pick the **highest priority** task assigned to backend_worker. This becomes your PRD. Set the status "InProgress", add a started_date ( including date and time ) in the index.json. If no slice has status planned, reply with:
15
+ 5. Pick the **highest priority** task assigned to backend_worker. This becomes your only task to work on. Set the status "InProgress", add a started_date ( including date and time ) in the index.json. If no slice has status planned, reply with:
14
16
  <promise>NO_TASKS</promise> and stop. Do not work on other slices.
15
- 6. Pick the slice definition from the project root /.slices in <folder> defined in the prd. Never work on more than one slice per iteration.
16
- 7. A slice can define additional prompts as codegen/backendPrompt. any additional prompts defined in backend are hints for the implementation of the slice and have to be taken into account. If you use the additional prompt, add a line in progress.txt
17
- 7. Define the slice type. If the processors-array is not empty, it´s an automation slice. Load the matching skill (automation-slice, state-change-slice, state-view-slice)
18
- 8. Write a short progress one liner after each step to progress.txt
19
- 9. Analyze and Implement that single slice, make use of the skills in the skills directory, but also your previsously collected
20
- knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition. Carefully inspect events, fields and compare against the implemented slice. JSON is the desired state. ATTENTION: A "planned" task can also be just added specifications. So always look at the slice itself, but also the specifications. If specifications were added in json, which are not on code, you need to add them in code.
21
- 10. The slice in the json is always true, the code follows what is defined in the json
22
- 11. the backend of a slice is only 'Done' if business logic is implemented as defined in the JSON, APIs are implemented, all scenarios in JSON are implemented in code and it
17
+ 6. Pick the slice definition from the project root /.slices in <folder> defined in the prd. Only work on this one assigned task, never other tasks.
18
+ 7. A slice can define additional prompts as codegen/backendPrompt in the slice.json. any additional prompts defined in json are hints for the implementation of the slice and have to be taken into account. If you use the additional prompt, add a line in progress.txt
19
+ 8. Define the slice type. If the processors-array is not empty, it´s an automation slice. if there is a readmodel, it´s a state view. If there is a command and no processor, it´s a state change. Load the matching skill (slice-automation, slice-state-change, slice-state-view)
20
+ 9. Write a short progress one liner after each step to progress.txt
21
+ 10. Analyze and Implement that single slice, make use of the skills in the skills directory, but also your previsously collected
22
+ knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition, whenever you work on a task. The definition can change for existing slices, which means the implementation has to adapt. Carefully inspect events, fields and compare against the implemented slice. JSON is the desired state. ATTENTION: A "planned" task can also be just added specifications. So always look at the slice itself, but also the specifications. If specifications were added in json, which are not on code, you need to add them in code.
23
+ 11. The slice in the json is always true, the code follows what is defined in the json
24
+ 12. the backend of a slice is only 'Done' if business logic is implemented as defined in the JSON, APIs are implemented, all scenarios in JSON are implemented in code and it
23
25
  fulfills the slice.json. There must be no specification in json, that has no equivalent in code.
24
- 12. make sure to write the ui-prompt.md for the ui_worker as defined if defined in the skill
25
- 13. Run quality checks ( npm run build, npm run test ) - Attention - it´s enough to run the tests for the slice. Do not run all tests.
26
- 14. even if the slice is fully implemented, run your test-analyzer skill and provide the code-slice.json file as defined in the skill
27
- 15. if the slice is an automation slice, set status to 'Done'. Otherwise - Update the Slice in the index.json back to status 'Planned' and assign the 'ui-worker'
28
- 16. If checks pass, commit ALL changes with message: `feat: [Slice Name]`.
29
- 17. if the slice stauts is 'Done', merge back.
30
- 17. Append your progress to `progress.txt` after each step in the iteration.
31
- 18. append your new learnings to AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
32
- 19. change the assignee to ui_worker and set it back to planned.
33
- 20. Finish the iteration.
26
+ 13. make sure to write the ui-prompt.md for the ui_worker as defined if defined in the skill
27
+ 14. Run quality checks ( npm run build, npm run test ) - Attention - it´s enough to run the tests for the slice. Do not run all tests.
28
+ 15. even if the slice is fully implemented, run your test-analyzer skill and provide the code-slice.json file as defined in the skill
29
+ 16. if the slice is an automation slice, set status to 'Done'. Otherwise - Update the Slice in the index.json back to status 'Planned' and assign the 'ui-worker'
30
+ 17. If checks pass, commit ALL changes with message: `feat: [Slice Name]`.
31
+ 18. Append your progress to `progress.txt` after each step in the iteration.
32
+ 19. append your new learnings to AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
33
+ 20. if the slice status is 'Done', merge back to main.
34
+ 21. Finish the iteration.
34
35
 
35
36
  ## Progress Report Format
36
37
 
@@ -4,11 +4,13 @@ Build React + TypeScript UI components from slice JSON definitions using establi
4
4
 
5
5
  ## Your Task
6
6
 
7
+ If a slice is in status 'planned', Even if a slice seems to have been implemented, make sure to verify the implementation, check for new fields, additional custom prompts in the slice json. A "planned" slice can also be an update of an existing slice. If that is the case, match the implemenetation to the updated slice definition.
8
+
7
9
  0. Do not read the entire code base. read /frontend/AGENTS.md. Focus on the tasks in this description.
8
10
  1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" and assigned to "ui_worker" is a task.
9
11
  2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
10
12
  3. Make sure you are on the right branch "feature/<slicename>", the branch should exist.
11
- 5. Pick the **highest priority** task assigned to ui_worker. This becomes your PRD. Set the status "InProgress", add a started_date ( including date and time ) in the index.json. If no slice has status planned, reply with:
13
+ 5. Pick the **highest priority** task assigned to ui_worker. This becomes your task. Set the status "InProgress", add a started_date ( including date and time ) in the index.json. If no slice has status planned, reply with:
12
14
  <promise>NO_TASKS</promise> and stop. Do not work on other slices.
13
15
  6. Pick the slice definition from the project root /.slices in <folder> defined in the prd. Never work on more than one slice per iteration.
14
16
  7. A slice can define additional prompts as codegen/uiPrompt. any additional prompts defined in backend are hints for the implementation of the slice and have to be taken into account. If you use the additional prompt, add a line in progress.txt
@@ -16,15 +18,15 @@ Build React + TypeScript UI components from slice JSON definitions using establi
16
18
  9. Analyze and Implement according to the Rest of the instructions in this file, make use of the skills in the skills directory, but also your previsously collected
17
19
  knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition.
18
20
  10. The slice in the json is always true, the code follows what is defined in the json
19
- 11. the slice is only 'Done' if APIs are implemented.
20
- 12. make sure to read the ui-prompt.md in /backend/src/slices/<slice>
21
- 13. Place the component where it belongs. If you can´t find a place, add a new page with /debug/<page> to showcase the component.
22
- 14. Run quality checks ( npm run build, tsc ) - Attention - it´s enough to run the tests for the slice. Do not run all tests.
21
+ 11. make sure to read the ui-prompt.md in /backend/src/slices/<slice>/
22
+ 13. carefully read custom uiPrompt in the slice.json and follow the instructions. If no mount point is defined in uiPrompt, verify if there is natural place where to mount the component. If there is no natural place, create a new page, name it after the Component and define a Route in App.tsx
23
+ 14. Run quality checks ( npm run build, tsc ) - Attention - the slice is only done if the component is mounted and testable.
24
+ 15. Make sure to style components according to the style guides.
23
25
  16. Update the Slice in the index.json to status 'Done' and remove assignment
24
- 17. If checks pass, commit ALL changes with message: `feat: [Slice Name]` and merge back to main as FF merge ( update
26
+ 17. Append your progress to `progress.txt` after each step in the iteration.
27
+ 18. append your new learnings to frontend/AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
28
+ 19. If checks pass, commit ALL changes with message: `feat: [Slice Name]` and merge back to main as FF merge ( update
25
29
  first )
26
- 18. Append your progress to `progress.txt` after each step in the iteration.
27
- 19. append your new learnings to frontend/AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
28
30
  20. Finish the iteration.
29
31
 
30
32
  ---
@@ -42,6 +44,7 @@ This project has custom skills available in `.claude/skills/` that automate comm
42
44
  - **`/ui-scaffold-component`**: Scaffold React components (List, Dialog, Page) using Bulma CSS
43
45
 
44
46
  **IMPORTANT**: When asked to build UI from slices, USE the `/ui-build-slice-ui` skill first! It will orchestrate all other skills in the correct order. Only use individual skills when you need to regenerate or modify a specific part.
47
+ **IMPORTANT**: Unless stated in a custom prompt, components read directly from the tables provided, not from api-endpoints via HTTP
45
48
 
46
49
  ---
47
50
 
@@ -5,8 +5,9 @@ You assign tasks to workers. Only one task can be assigned and in status planned
5
5
 
6
6
  ## Your Task
7
7
 
8
- 1. find the most important next slice by watching .slices/index.json
8
+ 1. find the most important next slice by reading .slices/index.json
9
9
  2. if the slice is in status planned and not assigned, assign it to backend_worker (property "assigned" ) and continue with backend/prompt.md. Ignore the rest of this file.
10
10
  3. if the status is in status "InProgress" and assigned to backend_worker, updated started_time and continue with backend/prompt.md. Ignore the rest of this file.
11
11
  4. if the status is "done" and assigned to "backend_worker", assign the task to "ui_worker" and move it back to status "planned". continue with frontend/prompt.md. Ignore the rest of this file.
12
- 4. if there is no task in status planned, return <promise>NO_TASKS</promise>
12
+ 5. if there is no task in status planned, return <promise>NO_TASKS</promise>
13
+ 6. If one task is done. Finish your work, do not continue with the next slice.