Claude
Skills
Sign in
Back

behavioral-core

Included with Lifetime
$97 forever

Plaited behavioral programming patterns for event-driven coordination, workflow control, and symbolic rule composition. Use when implementing behavioral programs with behavioral(), designing rule composition with bThread/bSync, orchestrating controllers, workflows, or agent loops, or building neuro-symbolic control layers.

AI Agents

What this skill does


# Behavioral Core

## Purpose

This skill teaches agents how to use Plaited's behavioral programming (BP) paradigm — an embedded TypeScript DSL for coordination where independent threads synchronize through events. Threads declare what they want (`request`), what they listen for (`waitFor`), and what they prohibit (`block`). The engine selects events that satisfy all threads simultaneously.

In this repo, BP is not only for symbolic reasoning. It is used as a general event-driven coordination model across controllers, workflows, agent loops, and rule systems. It is especially well suited to neuro-symbolic control, but that is only one of its uses.

**Use this when:**
- Implementing event-driven coordination with `behavioral()`
- Designing rule composition with `bThread`/`bSync`
- Understanding event selection, blocking, and priority
- Building safety constraints via additive blocking threads
- Orchestrating controllers, agent loops, workflows, or test runners
- Adding runtime rules without modifying existing threads

## Quick Reference

**Core APIs:**
- `behavioral()` — Create a behavioral program instance
- `bThread(rules, repeat?)` — Compose synchronization points into sequences
- `bSync({ request?, waitFor?, block?, interrupt? })` — Declare synchronization idioms
- `useFeedback()` — Register side-effect handlers (sync or async)
- `useSnapshot()` — Observe every BP engine decision (event selection, blocking)

**Code pattern:** In this repo, authors use the behavioral module APIs directly. Do not write raw generator functions or raw `yield` statements in repo code; express behavior with `bThread()` and `bSync()`, and treat generator mechanics as behavioral-core implementation detail.

**Testing:** BP logic is tested with Bun tests (`*.spec.ts`), not browser stories. See `src/behavioral/tests/` for examples.

## References

### Algorithm Reference

**[algorithm-reference.md](references/algorithm-reference.md)** — Deep reference for the BP algorithm as implemented in `src/behavioral/`. Covers:
- Core algorithm with formal definitions
- Priority-based event selection (Plaited's strategy)
- Super-step execution model and handler timing
- The `repeat` parameter (`true`, `false`, `() => boolean`)
- Ephemeral vs persistent blocks
- Shared state with block predicates
- Async feedback and the BP loop
- Infinite super-step anti-pattern
- Academic paper concepts (additive composition, scenario classification, pluggable ESM)

### Grounding Tests

The executable grounding for these patterns lives in the real runtime tests:

- `src/behavioral/tests/agent-patterns.spec.ts`
- `src/behavioral/tests/agent-lifecycle.spec.ts`
- `src/behavioral/tests/agent-orchestration.spec.ts`

Use those files when you need current repo-grounded BP examples. They are the
source of truth for validated coordination patterns; this skill should not
maintain copied test mirrors.

### Behavioral Programs Foundation

**[behavioral-programs.md](references/behavioral-programs.md)** — Conceptual BP foundation document. Use this when the embedded agent needs the general paradigm and the embedded DSL model:
- what behavioral programming is
- synchronization idioms (`request`, `waitFor`, `block`, `interrupt`)
- thread composition and lifecycle
- predicate usage and general non-UI coordination patterns

Use `algorithm-reference.md` when the embedded agent needs **Plaited-specific runtime semantics**:
- super-step timing
- priority behavior
- handler ordering
- persistent vs ephemeral blocks
- current repo-grounded coordination patterns

## Key Patterns

### Pattern 1: Phase-Transition Gate (taskGate)

The most important coordination pattern. A two-phase thread alternates between allowing and blocking events:

```typescript
bThreads.set({
  taskGate: bThread([
    // Phase 1: block task events, wait for 'task' to start
    bSync({ waitFor: 'task', block: (e) => TASK_EVENTS.has(e.type) }),
    // Phase 2: allow everything, wait for 'message' to end task
    bSync({ waitFor: 'message' }),
  ], true),  // loops: message → back to blocking
})
```

**Use for:** Task lifecycle gating, orchestrator routing, one-at-a-time enforcement. The thread's position in its rule sequence IS the coordination state — no external variables needed.

### Pattern 2: Per-Task Dynamic Threads

Threads added per task, interrupted when the task ends:

```typescript
useFeedback({
  task(detail) {
    bThreads.set({
      maxIterations: bThread([
        bSync({ waitFor: 'tool_result', interrupt: ['message'] }),
        bSync({ waitFor: 'tool_result', interrupt: ['message'] }),
        bSync({
          block: 'execute',
          request: { type: 'message', detail: { content: 'Max reached' } },
          interrupt: ['message'],
        }),
      ]),
    })
  },
})
```

**Key mechanics:** After interrupt, the thread name is freed for re-use. `bThread` without `repeat` = one-shot (terminates after last rule). Interrupt kills the thread wherever it is in the sequence.

### Pattern 3: Additive Safety Rules (Constitution)

Each safety rule is an independent blocking thread. New rules compose without touching existing ones:

```typescript
// Rule 1: block /etc/ writes
bThreads.set({
  noEtcWrites: bThread([
    bSync({
      block: (e) => e.type === 'execute' && e.detail?.path?.startsWith('/etc/'),
    }),
  ], true),
})

// Rule 2: block rm -rf (added later, doesn't modify rule 1)
bThreads.set({
  noRmRf: bThread([
    bSync({
      block: (e) => e.type === 'execute' && e.detail?.command?.includes('rm -rf'),
    }),
  ], true),
})
```

**Config-driven:** Rules can be loaded from arrays/JSON — each entry becomes a bThread with `repeat: true`.

### Pattern 4: Per-Call Dynamic Threads with Predicate Interrupt

Instead of persistent threads reading shared mutable state, each scoped operation gets its own guard thread that self-terminates via predicate interrupt:

```typescript
useFeedback({
  simulate(detail) {
    const id = detail.toolCall.id

    // Per-call guard: blocks execute until simulation completes
    bThreads.set({
      [`sim_guard_${id}`]: bThread([
        bSync({
          block: (e) => e.type === 'execute' && e.detail?.toolCall?.id === id,
          interrupt: [(e) => e.type === 'simulation_result' && e.detail?.toolCall?.id === id],
        }),
      ]),
    })

    // ... async simulation work ...
    trigger({ type: 'simulation_result', detail: { toolCall: detail.toolCall, prediction } })
    // sim_guard_{id} is interrupted (killed) by the simulation_result event
  },
})
```

**Key mechanics:**
- Block and interrupt both use **predicate listeners** scoped to a specific ID
- Thread self-terminates via interrupt — no shared state cleanup needed
- Thread name is unique per call → no collisions
- Observable: `SelectionBid.blockedBy: "sim_guard_tc-1"` and `SelectionBid.interrupts: "sim_guard_tc-1"` in snapshots

**Prefer for per-call scoped guards when lifecycle clarity matters.**
Persistent threads reading from mutable Sets/Maps can still be valid
coordination patterns, but they create more implicit coupling than a per-call
thread with explicit interrupt-based teardown.

### Pattern 5: Snapshot Observability

All BP decisions are observable via `useSnapshot`. `SelectionBid` records:
- `blockedBy: string` — which thread blocked this event
- `interrupts: string` — which thread was interrupted when this event was selected
- `selected: boolean` — whether this bid was the winning event
- `thread: string` — which thread proposed this bid

```typescript
// Persist snapshots → SQLite → model system prompt
useSnapshot((snapshot) => {
  if (snapshot.kind === 'selection') {
    for (const bid of snapshot.bids) {
      memory.saveEventLog({
        sessionId,
        eventType: bid.type,
        thread: bid.thread,
        selected: bid.selected,
        trigger: bid.trigger,
        priority: bid.priority,
        blockedBy: bid.blockedBy,     // "sim_guard_tc-1"
        interrupts: bid.interrupts,   // "sim_guard_tc-1"
        
Files: 3
Size: 97.2 KB
Complexity: 51/100
Category: AI Agents

Related in AI Agents