nerdsane/temper/temper-openclaw

Claude Code and agent skill by nerdsane/temper

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Install

npx vskill@latest install nerdsane/temper/temper-openclaw

SKILL.md

Temper — Your Organizational Backbone

Temper is where durable state lives. When you need to decide whether to handle something locally or route it through Temper, the test is simple: does this thing have a lifecycle? If it moves through stages, if someone might ask "what's the status?", if it matters tomorrow — it belongs in Temper.

Anything that has states and transitions — proposals, tasks, sessions, content jobs, incidents, deployments — belongs in Temper. Not a markdown file. Not a JSON blob. A verified state machine that enforces legal transitions, stores event history, and survives restarts.

The mental model:

  • Temper state is permanent. Create entities, fire actions, query state. This is your organizational backbone.
  • UIs are ephemeral. Build one when you need to show something to a human. Serve it, DM the URL, let it die when you're done. Next time, rebuild from Temper state — the data is still there.
  • Don't track state in markdown. If you're writing "Status: In Progress" in a .md file, that's a state machine pretending to be text. Make it real.
  • Route through Temper when it has a lifecycle. Anything that moves through stages becomes a capability you can evolve, compose with other tools, and share across agents. A shell script is a dead end — a Temper entity is a building block that gets better over time.

Anything that moves belongs here.


Getting Temper

bash
git clone https://github.com/nerdsane/temper.git ~/workspace/Development/temper
cd ~/workspace/Development/temper
Prerequisites
DependencyInstallWhy
Rust`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \sh`Temper is Rust
Z3brew install z3 (macOS) / apt install libz3-dev (Linux)L0 spec verification
Python 3Pre-installed on most systemsProxy server (serve.py)
Build

Z3 must be on the linker path or the final link fails. Set these before building:

bash
. "$HOME/.cargo/env"
# macOS (brew) — required, no exceptions
export Z3_SYS_Z3_HEADER="/opt/homebrew/include/z3.h"
export BINDGEN_EXTRA_CLANG_ARGS="-I/opt/homebrew/include"
export Z3_LIB_DIR="/opt/homebrew/lib"
export LIBRARY_PATH="/opt/homebrew/lib"
cargo build --release # ~60s on Mac mini M2

If you see ld: library 'z3' not found — you skipped the env vars. Set them and retry.

Storage — Turso (local file, zero config)

Temper uses Turso/libSQL as its storage backend. For local use, no env vars are needed. Temper defaults to ~/.local/share/temper/agents.db and creates it on first run:

bash
./target/release/temper serve --storage turso --port 3001
# Storage: turso (~/.local/share/temper/agents.db)

That's it. No credentials, no account, no config file.

Override the path if you want a different location (e.g. a shared workspace db):

bash
TURSO_URL="file:$HOME/workspace/apps/agents.db" \
./target/release/temper serve --storage turso --port 3001

Remote Turso (cloud, optional) requires both vars:

bash
export TURSO_URL="libsql://your-db.turso.io"
export TURSO_AUTH_TOKEN="your-token"

No Postgres to manage. No Redis to manage. Just a file.

Verify It Works
bash
cargo test # 647 tests
./target/release/temper serve --storage turso --port 3001
# OData API at <TEMPER_URL>/tdata
# (--app flag is optional: use it only to load specs at startup before first load-dir call)

What Temper Is

A Rust state machine backend. You define entities with states, actions, guards, and effects in IOA TOML. Temper gives you:

  • OData API — CRUD, bound actions (state transitions), SSE events
  • Turso persistence — state survives restarts via libSQL event log; all three backends (Turso, Redis, Postgres) support hydration on restart
  • Verification cascade — L0-L3 model checking at load time; illegal specs don't start
  • Multi-tenant — one server, many apps, isolated by X-Tenant-Id
  • OpenClaw plugin — real-time two-way connection for any OpenClaw agent (SSE → signal files → heartbeat wake)

When to Use This

  • You need state that survives restarts (not a JSON file, not markdown)
  • You have a workflow with defined states and transitions (proposals, content pipeline, task queue, releases)
  • You want verified transitions — illegal moves return 409, not silent corruption
  • You want a UI that reflects live state and lets your human interact
  • Multiple agents need to share state or hand off work

1. Write a Spec

Create apps/{your-app}/specs/{entity}.ioa.toml:

toml
[automaton]
name = "Task"
states = ["Open", "InProgress", "Done"]
initial = "Open"
[[state]]
name = "is_assigned"
type = "bool"
initial = "false"
[[action]]
name = "Assign"
kind = "input"
from = ["Open"]
to = "InProgress"
effect = ["is_assigned = true"]
[[action]]
name = "Complete"
kind = "internal"
from = ["InProgress"]
to = "Done"
guard = "is_assigned"
[[action]]
name = "Reopen"
kind = "input"
from = ["Done"]
to = "Open"
effect = ["is_assigned = false"]
[[invariant]]
name = "DoneRequiresAssignment"
assert = "status in ['Done'] => is_assigned"
Spec Reference

Actions — kind = "input" (human/dashboard-triggerable) vs kind = "internal" (agent-only). Both are callable via the OData API; the distinction is primarily for dashboard button rendering.

State variable types — only two are valid:

  • bool — initial = "false" or initial = "true"
  • counter — initial = "0" (integer, supports +=/-=/= effects and comparisons in guards)

int, string, float, and any other type will pass L0-L3 verification silently but the entity set will not register at runtime. Store text/numeric data via action params (they become entity fields automatically). Use state variables only for values that drive guards and invariants.

Guards — one expression checked before the transition fires, e.g. guard = "is_assigned && items > 0":

  • flag / !flag — boolean checks
  • count > 3, count <= limit — counter comparisons
  • &&, ||, !, =>, in [...], single-quoted strings — see docs/predicates.md

`to` is required on every action, including self-loops. A self-loop that keeps the entity in the same state needs to = "SameState" explicitly:

toml
[[action]]
name = "AddItem"
kind = "input"
from = ["Active"]
to = "Active" # required — even for self-loops
params = ["Item"]

Effects — effect is a list of statements applied on success, e.g. effect = ["items += 1", "is_assigned = true"]:

  • flag = true / flag = false / flag = params.p — set boolean
  • count += 1 / count -= 1 (stops at 0) / count = params.p — counter arithmetic; params.p is action parameter p
  • schedule('Action', 3600) — dispatch an action on this entity later; see docs/predicates.md#effects for lists and spawn

Nothing is implied by an action's name: an AddItem with no effect changes only status. Old forms ("set var true", "increment var", [[integration]]) fail to load; run temper migrate-predicates <files> to convert them.

Invariants — one assert expression, proven by verification in every reachable state; use status in [...] => ... to scope it to some states. Invariants may only read status, bool and counter variables; rules about text fields belong in [[field_invariant]], which is checked on writes.

Integrations (WASM) — when your app needs external API calls (payments, email, notifications), add a kind = "wasm" entry under the action's [[action.triggers]]:

toml
[[action]]
name = "ChargeCard"
from = ["Pending"]
to = "Charging"
[[action.triggers]]
name = "stripe_charge"
kind = "wasm"
module = "stripe_charge"
on_success = "ChargeSucceeded"
on_failure = "ChargeFailed"
[[action]]
name = "ChargeSucceeded"
kind = "input"
from = ["Charging"]
to = "Paid"
[[action]]
name = "ChargeFailed"
kind = "input"
from = ["Charging"]
to = "PaymentFailed"

The WASM module is a Rust cdylib compiled to wasm32-unknown-unknown and uploaded via POST /api/wasm/modules/{name}. See the developer skill (.claude/skills/temper-developer.md) for the full WASM module template.

Built-in module: `http_fetch` — for simple HTTP integrations, use the pre-compiled http_fetch module instead of writing custom WASM. Configure it directly on the trigger:

toml
[[action.triggers]]
name = "fetch_weather"
kind = "wasm"
module = "http_fetch"
on_success = "FetchSucceeded"
on_failure = "FetchFailed"
config = { url = "https://api.example.com/{param}", method = "GET" }

The http_fetch module reads url, method, and body from the trigger's config, makes the HTTP call via the host, and returns {"status_code": "200", "body": "..."} as callback params. URL templates support {param} substitution from action params.

Terminal states — list them in [automaton] (terminal = ["Done"]); verification then checks that no action leaves them. Entities in terminal states can't move, so design them intentionally.

L0–L3 Verification

At startup, Temper verifies every spec:

  • L0 — Z3 SMT: all guards satisfiable, invariants inductive, no dead states
  • L1 — Model check: full state space reachability, no deadlocks
  • L2 — Simulation: random action sequences, invariant holds across seeds
  • L3 — Property tests: 100 random runs, bounded depth

If verification fails, the server won't start. Fix the spec first.


2. Start the Server

One step, every session:

bash
curl -sf <TEMPER_URL>/tdata -H "X-Tenant-Id: _" > /dev/null 2>&1 \
&& echo "Temper already running" \
|| { nohup ./target/release/temper serve --storage turso --port 3001 --observe \
> /tmp/temper.log 2>&1 & sleep 4 && echo "Temper started"; }

The --observe flag auto-starts the Observe UI at the Observe UI. This is where humans approve or deny governance decisions (see Governance section below).

That's it. Temper auto-reloads your specs on startup — if you've called load-dir before, your entity types come back automatically. Your data was never gone (it's in the DB). No extra steps.

Verify it's serving your app:

bash
curl -s <TEMPER_URL>/tdata -H "X-Tenant-Id: your-app-name" | \
python3 -c "import sys,json; print([v['name'] for v in json.load(sys.stdin)['value']])"
First Time Only — Register Your App

The first time you run (new machine, new app), register your specs once:

bash
curl -s -X POST <TEMPER_URL>/api/specs/load-dir \
-H "Content-Type: application/json" \
-d "{\"tenant\": \"your-app-name\", \"specs_dir\": \"$HOME/workspace/apps/your-app/specs\"}"

Temper persists this mapping to ~/.local/share/temper/specs-registry.json. After that, every restart auto-loads — you never call this again unless you're adding a new app or changing your specs on disk.

Re-calling load-dir is safe and hot-swaps specs — existing entities keep their current state, the new spec takes effect immediately. Use it to iterate on specs without losing data.

⚠️ Spec changes are hot-reloads, not PRs. The activation path for spec changes is:

  1. Edit the .ioa.toml / .csdl.xml file
  2. Call load-dir (or submit_specs via MCP)
  3. Verification runs → spec takes effect immediately

Git-commit for version tracking. But do NOT create pull requests for spec-only changes. PRs are for Temper platform code (Rust). Specs are your tools — you iterate on them live.


3. Use the OData API

Base URL: <TEMPER_URL>/tdata Required header: X-Tenant-Id: {your-app}

Action URL Format — Read This First

Firing an action on an entity:

POST /tdata/{EntitySet}('{entity-id}')/Temper.{ActionName}

The `Temper.` prefix is mandatory. This is OData bound action syntax. Without it, you get 404.

bash
# ✅ correct
curl -X POST "<TEMPER_URL>/tdata/Tasks('abc-123')/Temper.Assign" \
-H "Content-Type: application/json" -H "X-Tenant-Id: my-app" \
-d '{"AssignedTo": "haku"}'

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
46
Repository forks
10
7-day trend score
19

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/nerdsane/temper/temper-openclaw/badge)](https://verified-skill.com/skills/nerdsane/temper/temper-openclaw)

Something wrong with this skill or its page? Tell us.