Skip to content
cccskills
FreePublished on GitHub

grain-command-handler

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

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md3.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Grain Command Handler Pattern

Required Imports

(ns my.service.core.commands
  (:require [ai.obney.grain.event-store-v3.interface :refer [->event]]
            [ai.obney.grain.command-processor-v2.interface :refer [defcommand]]
            [ai.obney.grain.command-processor-v2.interface :as cp]
            [ai.obney.grain.time.interface :as time]
            [my.service.core.read-models :as rm]
            [cognitect.anomalies :as anom]))

Command Template

(defcommand :service command-name
  "Docstring describing what the command does."
  [{{:keys [entity-id field1 field2]} :command
    :as ctx}]

  ;; 1. Read current state from read models (pass ctx, NOT bare event-store)
  (let [entity (rm/get-entity ctx entity-id)]
    (cond
      ;; Return anomaly for validation failures
      (not entity)
      {::anom/category ::anom/not-found
       ::anom/message "Entity not found"}

      (not (:active entity))
      {::anom/category ::anom/conflict
       ::anom/message "Cannot modify inactive entity"}

      ;; Success: return events
      :else
      {:command-result/events
       [(->event
         {:type :service/entity-updated
          :tags #{[:entity entity-id]}
          :body {:entity-id entity-id
                 :field1 field1
                 :field2 field2
                 :modified-at (str (time/now))}})]})))

Key Variations

Success with Events Only

{:command-result/events
 [(->event {:type :service/thing-created
            :tags #{[:thing thing-id]}
            :body {:thing-id thing-id :name name}})]}

Anomaly Return (Error)

{::anom/category ::anom/conflict
 ::anom/message "Name already taken."}

CAS (Optimistic Concurrency)

Prevents races by checking state hasn't changed between read and write:

{:command-result/events [...]
 :command-result/cas {:types thing-event-types
                      :predicate-fn (fn [_events]
                                      (= :active (:status (rm/get-thing ctx id))))}}

Calling Other Commands

(let [result (cp/process-command
              (assoc ctx :command {:command/id (random-uuid)
                                   :command/timestamp (time/now)
                                   :command/name :other/command-name
                                   :field1 value1}))]
  ;; result is either anomaly or {:command-result/events [...]}
  result)

Common Anomaly Categories

  • ::anom/not-found -- resource does not exist
  • ::anom/conflict -- business rule violation (duplicate, invalid state)
  • ::anom/incorrect -- bad input data
  • ::anom/forbidden -- not authorized for this action

Conventions

  • Commands modify state by emitting events; never write to a database directly
  • Always validate via read models before emitting events
  • Read model helpers take ctx (the full context), not bare event-store
  • Tag events with #{[:entity entity-id]} for efficient filtered reads
  • Return anomalies for all error cases
  • Include modified-at in event bodies when relevant

Reference Files

  • components/orc-service/src/ai/obney/orc/orc_service/core/commands.clj -- ORC sheet commands
  • components/colbert/src/ai/obney/orc/colbert/core/commands.clj -- ColBERT commands
  • components/ontology/src/ai/obney/orc/ontology/core/commands.clj -- Ontology commands

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

allium

Free

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

The English summary is being prepared. Showing the original description.

ObneyAI/orc22Updated Oct 8, 2026

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.

The English summary is being prepared. Showing the original description.

ObneyAI/orc22Updated Oct 8, 2026

elicit

Free

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.

The English summary is being prepared. Showing the original description.

ObneyAI/orc22Updated Oct 8, 2026

Create scheduled background jobs that run on cron schedules

The English summary is being prepared. Showing the original description.

ObneyAI/orc22Updated Oct 8, 2026

Implement query handlers that read from event-sourced read models

The English summary is being prepared. Showing the original description.

ObneyAI/orc22Updated Oct 8, 2026

Build read models by reducing events into queryable state

The English summary is being prepared. Showing the original description.

ObneyAI/orc22Updated Oct 8, 2026

See all skills by ObneyAI

Report a problem with this skill