본문으로 건너뛰기
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

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

allium

무료

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

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 20일 업데이트

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/orc222026년 10월 20일 업데이트

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/orc222026년 10월 20일 업데이트

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

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 20일 업데이트

Create scheduled background jobs that run on cron schedules

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 20일 업데이트

Implement query handlers that read from event-sourced read models

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 20일 업데이트

ObneyAI의 스킬 모두 보기

이 스킬의 문제 신고