Schematics
← The library family

Behavior as data

@schema-reflection/logic

Published · 0.1.0

See what an action does before it does it.

Before 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

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

What it unlocks

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.

Test the effects that users care about

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.

Keep execution services at the application edge

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

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.

  1. Name a domain action and its boundary

    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.

  2. Inspect and test the tree

    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.

  3. Provide real services deliberately

    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.

Published · 0.1.0pnpm 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.

approve-candidate.tsnpm API
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 it
▶ try itsimulated in your browser

Interactive 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

In practice

ApproveCandidate becomes more than a tool name.

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.

  1. Review

    The printed definition shows require, set, emit, and get in sequence. The behavior is visible before the host binds it to live services.

  2. Exercise

    Run it with a reviewer and with a reader. Assert on the result, state, events, and the failure from the rejected invocation.

  3. Execute

    The application supplies the storage and event services and invokes the approved definition at its authorization boundary.

05 How it composes

How it composes

Each library owns one kind of meaning. Combine the ones your application needs, with an explicit boundary between their jobs.

  • logic + predicates

    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.

  • logic + algebra

    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.

  • logic + workflow

    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.

  • logic + core

    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.

  • logic + filesystem

    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

Where it stops

The published interpreter is not a transaction manager

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.

A syntax tree has a supported language

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

One library at a time.

Try the Schematics playground →