本文へ移動
cccskills
無料GitHub で公開

grain-todo-processor

Implement event-driven side effects that react to domain events

インストール方法を見る

含まれるファイル(1)

  • SKILL.md5.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Grain Todo Processor Pattern

Todo processors are policies that react to domain events and emit follow-up events or perform side effects.

Required Imports

(ns ai.obney.orc.my-service.core.todo-processors
  (:require [ai.obney.orc.my-service.core.read-models :as rm]
            [ai.obney.grain.event-store-v3.interface :refer [->event]]
            [ai.obney.grain.todo-processor-v2.interface :refer [defprocessor]]
            [ai.obney.grain.time.interface :as time]))

Processor Template

(defprocessor :ns processor-name
  {:topics #{:ns/triggering-event}}
  "Docstring explaining what this processor does and when."
  [context]
  (let [event (:event context)
        entity-id (:entity-id event)
        ;; Check current state using read models
        entity (rm/get-entity context entity-id)]
    (if (should-take-action? entity)
      ;; Emit follow-up events
      {:result/events
       [(->event
         {:type :ns/follow-up-action
          :tags #{[:entity entity-id]}
          :body {:entity-id entity-id
                 :triggered-by (:event/type event)
                 :processed-at (str (time/now))}})]}
      ;; No action needed
      {})))

The generated var name is ns-processor-name (e.g., (defprocessor :crm ensure-attribution ...) creates var crm-ensure-attribution).

Return Patterns

Emit Events (pure -- batch checkpointed)

{:result/events [event1 event2]}

Side Effect with Checkpoint Control

{:result/effect (fn [] (send-email! ...))
 :result/checkpoint :after}  ;; or :before

No Action

{}

Auto-Registration

defprocessor registers the processor in a global registry automatically. There is no manual registry map (def todo-processors) needed. The control plane discovers processors from the registry and starts them when a tenant is assigned to the node.

Interface.clj only needs a bare side-effect require to load the namespace:

(ns ai.obney.orc.my-service.interface
  (:require ;; Side-effect requires
            [ai.obney.orc.my-service.core.commands]
            [ai.obney.orc.my-service.core.queries]
            [ai.obney.orc.my-service.core.todo-processors]  ;; bare require, no alias
            ...))

;; No todo-processors re-export needed

No manual wiring is needed -- the control plane discovers processors from the global registry.

Common Use Cases

Ensure Default Value

(defprocessor :crm ensure-attribution
  {:topics #{:crm/contact-created}}
  "Record default attribution if none exists."
  [context]
  (let [{:keys [contact-id]} (:event context)
        existing (rm/get-contact context contact-id)]
    (if (:attribution existing)
      {}  ;; Already has attribution
      {:result/events
       [(->event
         {:type :crm/attribution-recorded
          :tags #{[:contact contact-id]}
          :body {:contact-id contact-id
                 :attribution {:source :manual_entry
                               :recorded-at (str (time/now))}}})]})))

Duplicate Detection

(defprocessor :crm check-for-duplicates
  {:topics #{:crm/contact-created}}
  "Check for duplicates when contact is created."
  [context]
  (let [{:keys [contact-id]} (:event context)
        email (normalize-email (get-in (:event context) [:field-values :email]))
        existing (rm/get-contacts-by-email context email)]
    (if (seq (remove #(= contact-id (:id %)) existing))
      {:result/events
       [(->event
         {:type :crm/duplicate-detected
          :tags #{[:contact contact-id]}
          :body {:contact-id contact-id
                 :match-type :email
                 :match-value email}})]}
      {})))

Cascade Operations

(defprocessor :crm end-relationships-on-archive
  {:topics #{:crm/contact-archived}}
  "End all relationships when contact is archived."
  [context]
  (let [{:keys [contact-id]} (:event context)
        rels (rm/get-relationships-for-contact context contact-id)
        active-rels (filter #(nil? (:end-date %)) rels)]
    (if (seq active-rels)
      {:result/events
       (mapv (fn [rel]
               (->event
                {:type :crm/relationship-ended
                 :tags #{[:relationship (:id rel)]}
                 :body {:relationship-id (:id rel)
                        :end-date (str (java.time.LocalDate/now))
                        :reason "Contact archived"}}))
             active-rels)}
      {})))

Transform Source Data

(defprocessor :crm record-lead-attribution
  {:topics #{:crm/lead-captured}}
  "Map lead form type to attribution source."
  [context]
  (let [{:keys [contact-id form-type form-id]} (:event context)
        source (case form-type
                 :application :application_form
                 :intake :intake_form
                 :interest :interest_form
                 :manual_entry)]
    {:result/events
     [(->event
       {:type :crm/attribution-recorded
        :tags #{[:contact contact-id]}
        :body {:contact-id contact-id
               :attribution {:source source
                             :form-id form-id}}})]}))

Conventions

  • Processors receive a context map with :event, :event-store, :tenant-id
  • Use read models to check current state before acting
  • Return {:result/events [...]} or {} (empty map)
  • One processor can listen to multiple topics via the :topics set
  • Multiple processors can listen to the same topic
  • Normalize data before comparison (email lowercase, phone digits only)
  • Include reason/context in generated events for audit trail

Reference Files

  • components/user-service/src/ai/obney/workshop/user_service/core/todo_processors.clj - Email notifications
  • components/crm-service/src/ai/obney/workshop/crm_service/core/todo_processors.clj - Attribution & duplicates

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

allium

無料

Give your AI agents something more useful than a prompt. Velocity through clarity.

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

distill

無料

Extract an Allium specification from an existing codebase. Use when the user has existing code and wants to distil behaviour into a spec, reverse engineer a specification from implementation, generate a spec from code, turn implementation into a behavioural specification, or document what a codebase does in Allium terms.

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

elicit

無料

Run a structured discovery session to build an Allium specification through conversation. Use when the user wants to create a new spec from scratch, elicit or gather requirements, capture domain behaviour, specify a feature or system, define what a system should do, or is describing functionality and needs help shaping it into a specification.

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

Implement command handlers that validate state and emit events using the Grain framework

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

Create scheduled background jobs that run on cron schedules

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

Implement query handlers that read from event-sourced read models

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

ObneyAI のスキルをすべて見る

このスキルの問題を報告する