Review behavior before execution
Print the action or inspect its tree to see requirements, state operations, and events. Human review and tool descriptions can be based on the action's definition rather than a separately maintained summary.
Behavior as data
@schema-reflection/logic
Published · 0.1.0Before a person or agent runs an action, they should be able to see what it means. Make the action's requirements, writes, and events part of its definition.
01 Why it matters
A function can approve a candidate, but its name is not a description of its behavior. A reviewer has to trace calls to discover the permission check, the fields it writes, and the event it emits. An agent calling the function has the same gap between the tool's label and its actual effects.
Logic expresses the action as a syntax tree built from explicit primitives. The builder constructs that tree; the interpreter executes it. That separation gives tools something to print, serialize, and test before an action runs, and gives your application a place to provide storage and event handling.
02 What it unlocks
Print the action or inspect its tree to see requirements, state operations, and events. Human review and tool descriptions can be based on the action's definition rather than a separately maintained summary.
Run an action against in-memory state and assert on its result, resulting state, and recorded events. A test can distinguish a successful return from the correct business transition.
Use the same action definition with host-provided Store and EventSink services. The host owns how state is persisted and how events are delivered.
03 How to use it
Choose one action with a clear input, a small state change, and an event. Make its tree reviewable, prove its observable behavior in memory, and then integrate the host services you need.
Start with a single operation such as ApproveCandidate. Attach input/output schemas where needed, and express its requirements, writes, events, and result using the builder primitives.
Use Logic.print to review it, Logic.serialize to carry executable structure, and Logic.run for in-memory tests. In the published API, serialized structure does not carry the attached input/output schemas.
Integrate Logic.evaluate into an Effect application with your own Store and EventSink. Decide how persistence, failure, and retries should work before exposing the action through an API or agent tool.
pnpm add @schema-reflection/logic@0.1.0View the npm release ↗The published 0.1.0 release declares Effect 4.0.0-rc.113. Pin the release and use its API rather than assuming the experimental portable API is in that npm version.
import { Logic } from "@schema-reflection/logic"
const ApproveCandidate = Logic.Action("ApproveCandidate", ($) =>
$.do(
$.require($.eq($.ref.actor.role, "reviewer"), "forbidden"),
$.set($.ref.candidate.status, "approved"),
$.emit("candidate.approved", { id: $.ref.candidate.id }),
$.get($.ref.candidate.status),
),
)
Logic.print(ApproveCandidate) // review it
Logic.serialize(ApproveCandidate) // store it
await Logic.run(ApproveCandidate, { actor, candidate }) // test itInteractive simulation loads when this panel comes into view.
The interactive panel is a Foldkit/Foldworks simulation of the behavior, not an execution of this package. The definition above is an API excerpt; supply your application's domain values and contracts.
04 In practice
A reviewer approves a pending candidate. The action needs to check the caller, change the status, emit an event, and return a result that the caller can confirm.
The printed definition shows require, set, emit, and get in sequence. The behavior is visible before the host binds it to live services.
Run it with a reviewer and with a reader. Assert on the result, state, events, and the failure from the rejected invocation.
The application supplies the storage and event services and invokes the approved definition at its authorization boundary.
05 How it composes
Each library owns one kind of meaning. Combine the ones your application needs, with an explicit boundary between their jobs.
A policy provides the decision; logic describes the behavior permitted by it. Evaluate the policy at the action boundary or adapt its supported condition language into action expressions.
Use relationship validation on configuration read or produced by an action. It answers whether identifiers resolve, while logic answers what the action does. The host decides when to check and whether to commit.
A process can request an action and wait for its result. The host binds workflow commands to permitted action handlers; it does not execute arbitrary uploaded definitions by name. Match the portable prototype's contract to the executor you use.
Core supports the experimental portable API that bundles shapes with behavior. The published Action/serialize API shown here carries the tree but not attached boundary schemas; treat those formats as distinct.
The proposed file layer could validate stored action documents and their links before a host loads them. Validating a file and permitting its execution remain separate host decisions.
These are application composition patterns, not a promise of automatic adapters. Align Effect peers and artifact formats before combining runtimes; source prototypes can differ from published releases.
See the family's approval scenario →06 Where it stops
Writes and events that occur before a failure can remain applied. A retry can repeat them. Provide the persistence semantics and idempotency your real services require.
Use the defined primitives and expression forms. Arbitrary closures are not portable action data. The newer experimental typed portable API differs from the published legacy API shown in the example.
All libraries are pre-1.0. Pin exact versions and plan for changes to APIs and artifact formats.
Continue with the piece next to it