Skip to main content

tla-precheck

Design and verify state machines using the TLA PreCheck TypeScript DSL. Use when building billing flows, subscription lifecycles, agent orchestration, queue processing, deployment pipelines, or any critical state machine where a bug means corrupted data, stuck users, or silent failures. Triggers on .machine.ts files, state machine design tasks, or when formal verification of state transitions is needed.

Ir para a instalação

Informações da origem

Repositório
kingbootoshi/tla-precheck
Última atividade na origem
16 de março de 2026 às 03:26
Idioma detectado do SKILL.md
inglês
Estrelas
112
Forks
5

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
3 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
tla-precheck
description
Design and verify state machines using the TLA PreCheck TypeScript DSL. Use when building billing flows, subscription lifecycles, agent orchestration, queue processing, deployment pipelines, or any critical state machine where a bug means corrupted data, stuck users, or silent failures. Triggers on .machine.ts files, state machine design tasks, or when formal verification of state transitions is needed.
# TLA PreCheck ## What This Is TLA PreCheck is a restricted TypeScript DSL with TLA+ semantics. You write machines in `.machine.ts` files. You do NOT write `.tla` files. The compiler generates: - TLA+ spec for TLC model checking (exhaustive proof) - TypeScript interpreter for runtime execution - Postgres DDL for database-level enforcement - Generated adapter with typed per-action functions The key guarantee: for a chosen finite proof tier, the generated TLA+ and the generated TypeScript interpreter reach the **same state graph**. Not similar - identical. Mathematically verified. Think in TLA+, write in TypeScript. ## Design Rules 1. **One risky workflow per machine.** Don't model your whole system. Model the billing state machine. Model the subscription lifecycle. One machine per critical flow. 2. **Keep proof domains tiny.** 2 users, 3 runs finds most bugs. Scale up in nightly tiers. 3. **Fix the design, not the code.** If `check` fails, the state machine design is wrong. Redesign the transitions and invariants. 4. **Never edit generated artifacts.** Don't touch `.tla` files, certificates, or adapter code. Regenerate with `build`. 5. **Never write directly to machine-owned tables.** All mutations go through the generated adapter or interpreter. 6. **Prefer small atomic machines.** Multiple small machines composed at the application layer beat one giant spec. ## The Three Commands ```bash # 1. Start a new machine npx tla-precheck init # 2. Design loop - run until it passes npx tla-precheck check <machine> # 3. Ship it - generates adapter + all artifacts for adapter-capable machines npx tla-precheck build <machine> ``` **The design loop:** 1. Write/edit the `.machine.ts` DSL 2. Run `check` - it validates, estimates state space, then runs TLC 3. If the model checker finds a bug (invariant violation, stuck state), it tells you exactly what sequence of events caused it 4. Fix the DSL - not a code patch, a design fix 5. Repeat until `check` passes 6. If the machine declares `metadata.runtimeAdapter`, `metadata.ownedTables`, and `metadata.ownedColumns`, run `build` to generate the adapter and all artifacts 7. Import the generated adapter functions into your codebase ## DSL Quick Reference Machines have **variables**, **actions**, **invariants**, and **proof tiers**. If you want `build` to generate a database adapter, the machine also needs adapter metadata. ### Variables ```typescript // Scalar: one value status: scalarVar(enumType("draft", "active", "done"), lit("draft")) // Map: one value per domain element (like a column per row) status: mapVar("Runs", enumType("idle", "running", "done"), lit("idle")) owner: mapVar("Runs", optionType(domainType("Users")), lit(null)) ``` ### Actions ```typescript activate: { params: { r: "Runs" }, // bound parameters guard: eq(index(status, param("r")), lit("idle")), // when is this allowed? updates: [setMap("status", param("r"), lit("running"))] // what changes? } ``` ### Invariants ```typescript oneActivePerUser: { description: "At most one running item per user", formula: forall("Users", "u", lte( count("Runs", "c", and( eq(index(owner, param("c")), param("u")), eq(index(status, param("c")), lit("running")) )), lit(1) )) } ``` ### Proof Tiers ```typescript proof: { defaultTier: "pr", tiers: { pr: { domains: { Users: modelValues("u", { size: 2, symmetry: true }), Runs: ids({ prefix: "r", size: 3 }) }, budgets: { maxEstimatedStates: 10_000 } } } } ``` For the full DSL reference, see `references/dsl-cheatsheet.md`. For the complete CLI workflow, see `references/cli-workflow.md`. ## What "Done" Means The machine is verified when: 1. `check` passes - TLC exhaustively explored every reachable state 2. The equivalence certificate says `equivalent: true` 3. `build` succeeds - adapter generated from the proven machine when adapter metadata is declared After `build`, your codebase imports typed functions: ```typescript import { create, cancel, complete } from "./machine-adapters/MyMachine.adapter"; await create(sql, { u: userId, r: runId }); ``` Each function opens a transaction, locks rows, runs the proven interpreter, diffs state, and writes changes. No hand-written guard logic. No hallucination surface. ## Runtime Boundary - The interpreter IS the runtime semantics - not an advisory check - The generated adapter is the preferred mutation path when the machine fits the adapter subset (single owned table, all mapVars, one row domain) - `build` requires explicit database mapping metadata: - `metadata.runtimeAdapter` - `metadata.ownedTables` - `metadata.ownedColumns` - If the adapter subset doesn't fit, call the interpreter manually: ```typescript import { resolveMachine } from "tla-precheck/proof"; import { buildInitialState, enabled, step } from "tla-precheck/interpreter"; const resolved = resolveMachine(myMachine, "pr"); const current = buildInitialState(resolved); if (!enabled(resolved, current, "activate", { r: "r1" })) { throw new Error("Transition not enabled"); } const next = step(resolved, current, "activate", { r: "r1" }); ``` - Storage constraints (Postgres partial unique indexes, CHECK constraints) back cross-row invariants at the database level
Ver no GitHub