Skip to main content

automation-slice

builds an automation slice from an event model

Source facts

Repository
dilgerma/nebulit-code-generators
Last source activity
February 16, 2026 at 20:00
Detected SKILL.md language
English
Stars
22
Forks
10

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
automation-slice
description
builds an automation slice from an event model
### Critical understanding Make sure to read the Agents.md file before building anything. If the processors-Array is not empty, it´s an automation slice. Important - an automation slice is "just" a state-change slice with an additional automation that triggers the command. So also read the skill for 'state-change-slice' ## Critical Requirements ### Restaurant ID Requirement - **CRITICAL**: ALL events MUST have `restaurantId` in their metadata (camelCase) - **NEVER** use `locationId` or `location_id` - these are outdated and forbidden - **CRITICAL**: ALL database tables (including TODO lists) MUST have a `restaurant_id` column (snake_case) - This ensures proper multi-tenancy and data isolation ## Overview Automations are processes that happen in the background, based on a TODO List. TODO Lists are always tables (read models) and each row in the table is a TODO Item. The automation is only responsible to: 1. Fetch items from the TODO List (read model) 2. Fire the command for each item 3. Repeat on a schedule using CRON ## Architecture Pattern ``` ┌─────────────────┐ │ TODO List │ (Read Model - Inbound Dependency) │ "items to do" │ Example: "clerks_to_invite", "items_to_fetch" └────────┬────────┘ │ │ reads │ ┌────────▼────────┐ │ processor.ts │ (CRON scheduled automation) │ │ - Fetches TODO items │ │ - Fires commands └────────┬────────┘ │ │ invokes │ ┌────────▼────────┐ │ CommandHandler │ (State-change logic) │ Command.ts │ - decide() function │ routes.ts │ - evolve() function └─────────────────┘ ``` ## Implementation Structure An automation slice consists of: 1. **processor.ts** - CRON automation that fetches TODO items and fires commands 2. **[Command]Command.ts** - Command handler with decide/evolve logic 3. **routes.ts** - HTTP API endpoint for manual command invocation ## Processor Configuration Two patterns exist in the codebase: ### Pattern 1: Using startProcessor helper (Recommended) ```typescript import {ProcessorConfig, ProcessorTodoItem, startProcessor} from "../../process/process"; import {handleYourCommand} from "./YourCommandCommand"; export type ItemToProcess = { itemId: string, // other fields from read model } const config: ProcessorConfig = { schedule: "*/5 * * * * *", // Every 5 seconds (cron format) endpoint: "your-todo-list-collection", // Read model endpoint query: { "status": "OPEN", // Filter criteria "_limit": "1" // Process one at a time } } const handler = async (item: ItemToProcess & ProcessorTodoItem) => { console.log(`Processing item: ${item.itemId}`) try { await handleYourCommand(`aggregate-${item.itemId}`, { type: "YourCommand", data: { itemId: item.itemId, // map other fields }, metadata: {} }) console.log(`Successfully processed item: ${item.itemId}`) } catch (error) { console.error(`Error processing item ${item.itemId}:`, error) } } export const processor = { start: () => { console.log("[YourProcessor] Starting processor...") startProcessor<ItemToProcess>(config, handler) } } ``` ### Pattern 2: Direct Supabase query (Legacy) ```typescript import {ProcessorConfig} from "../../process/process"; import {YourCommand, handleYourCommand} from "./YourCommandCommand"; import cron from "node-cron"; import {createServiceClient} from "../../supabase/api"; const config: ProcessorConfig = { schedule: '*/30 * * * * *', // Every 30 seconds endpoint: "your_todo_table", // Supabase table name } export const processor = { start: () => { cron.schedule(config.schedule, async () => { console.log("Running process") let client = createServiceClient() let result = await client.from(config.endpoint).select("*") if (result.count == 0) { console.log(`Nothing to do for ${config.endpoint}`) return; } for (const item of result.data ?? []) { const command: YourCommand = { type: "YourCommand", data: { itemId: item.itemId! }, metadata: {} } const id = item.itemId if (!id) { throw `Cannot process Command ${command.type}. No Id available.` } await handleYourCommand(id, command) } }) } } ``` ## CRON Schedule Format ``` ┌───────────── second (0-59) │ ┌─────────── minute (0-59) │ │ ┌───────── hour (0-23) │ │ │ ┌─────── day of month (1-31) │ │ │ │ ┌───── month (1-12) │ │ │ │ │ ┌─── day of week (0-7) │ │ │ │ │ │ * * * * * * ``` Common schedules: - `*/5 * * * * *` - Every 5 seconds - `*/30 * * * * *` - Every 30 seconds - `0 */1 * * * *` - Every minute - `0 0 * * * *` - Every hour ## Real Examples from Codebase ### Example 1: ConfirmInvitation (Clerk management) **TODO List Read Model**: `clerks_to_invite` table **Automation**: `src/slices/ConfirmInvitation/processor.ts` **Command**: `ConfirmInvitationCommand` **Event Emitted**: `ClerkInvitationConfirmed` ```typescript // processor.ts const config: ProcessorConfig = { schedule: '*/30 * * * * *', endpoint: "clerks_to_invite", } // Fetches clerks from TODO list and confirms their invitations for (const clerk of result.data ?? []) { const command: ConfirmInvitationCommand = { type: "ConfirmInvitation", data: { clerkId: clerk.clerkId! }, metadata: {} } await handleConfirmInvitation(clerk.clerkId, command) } ``` in each slice folder, generate a file .slice.json ``` { "id" : "<slice id>", "slice": "<slice title>", "context": "<contextx>", "link": "https://miro.com/app/board/<board-id>=/?moveToWidget=<slice id>" } ``` ## Key Points 1. **TODO List Naming**: Use format `<things>_to_<action>` (e.g., `clerks_to_invite`, `items_to_fetch`) 2. **Read Model Endpoint**: TODO list is accessed via `/api/query/<name>-collection` endpoint 3. **Processor Types**: Define TypeScript types for TODO items matching read model structure 4. **Error Handling**: Always wrap command execution in try-catch, log errors but continue processing 5. **Schedule Wisely**: Balance responsiveness vs system load (30 seconds is common for production) 6. **Status Management**: TODO list typically has a `status` field ("OPEN", "PROCESSING", "DONE") 7. **Limit Processing**: Use `_limit: "1"` to process one item at a time, preventing concurrent issues 8. **Stream Naming**: Use aggregate-based stream names (e.g., `catalogue-${itemId}`) ## Implementation Steps 1. **Read the input JSON** from templates/sample-input.json 2. **Create processor.ts** following one of the patterns above 3. **Implement state-change slice** using the 'state-change-slice' skill - Creates `[Command]Command.ts` with decide/evolve functions - Creates `routes.ts` for HTTP API endpoint 4. **Register processor** in main application startup 5. **Ensure TODO List exists** as a read model (separate slice handles this) ## Sample Input Structure Sample input: read 'templates/sample-input.json' The input defines: - **processors[]**: Array of automation definitions - title: Automation name - dependencies: Inbound (TODO list) and Outbound (Command) connections - **commands[]**: Commands triggered by the automation - **events[]**: Events emitted by the commands
View on GitHub