Skip to main content
ThunderLang
Draft documentation. Syntax and behavior are illustrative and will change before v1.

Testing and verification

Testing is a first-class part of ThunderLang, not something delegated entirely to Jest, PyTest, or JUnit.

ThunderLang tests prove that an implementation matches intent.

A traditional test asks "does this code produce the expected result?" ThunderLang additionally asks: does the behavior match the declared goal, are all guarantees enforced, are prohibited behaviors impossible, does every target behave consistently, did an AI change introduce drift, and what evidence proves the change is safe.

The public promise:

Write the requirement once. Run it as a test. Prove every implementation still matches.


What works today

The deterministic compiler already ships a real, tested slice of this. All of the following run offline with no AI:

Command What it does
thunder test <file> Runs example and decision tests (test / case / given / expect) through the deterministic interpreter, before any target code exists. --json for machine output.
thunder test <file> --contracts Derives one obligation from every guarantee and never, and resolves each honestly: PASS when a verification is declared and the file's checks are green, UNVERIFIED when nothing verifies it, FAIL when a check fails. Add --strict to fail the run on any UNVERIFIED obligation (CI gate: nothing left unproven).
thunder coverage <file> Reports how many declared claims carry a verification.
thunder prove <file> Runs the tests, evaluates every guarantee and never as an obligation, and emits a durable intent-proof-v1 artifact. Unverified claims are reported as UNVERIFIED, never as passed. --json for the raw proof.
thunder verify <proof.json> <source> Re-checks a proof against its source (hash + claims) to detect tampering or drift.

Example, honest by construction:

$ thunder prove CreateInvoice.thunder
Proof created: proof-176a82

  Intent:        CreateInvoice
  Intent hash:   sha256:176a82...
  Compiler:      ThunderLang 0.1.x
  Syntax:        PASS
  Semantics:     PASS
  Guarantees:    3/3 carry a verification
  Prohibitions:  1/2 carry a verification  (1 UNVERIFIED)
  Proof status:  DRAFT  (has unverified claims)

  UNVERIFIED (this is where drift hides):
    - never create-invoice-for-unapproved-order: create invoice for unapproved order

The rest of this document is the design ThunderLang is being built toward. It is a roadmap, not a claim of current capability.


The four test types

1. Example tests

Specific inputs, expected outputs. Runnable through the interpreter today.

test CanEnroll
  case qualified adult
    given
      age: 20
      score: 90
    expect
      result == Eligible
      matchedRule == qualifiedAdult

2. Contract tests

Every guarantee and never is a test obligation. Developers should not rewrite each declared guarantee as a conventional test; the compiler derives it. This is one of ThunderLang's biggest differentiators.

3. Property tests

Example tests cover known cases; property tests explore many generated cases and shrink to the smallest reproducible failure.

4. Scenario tests

Workflows across components (given / when / then / never), the ThunderLang equivalent of acceptance and behavior testing, but connected to the canonical Intent Graph.


Two execution modes

  • Semantic mode (--mode semantic): run decisions, rules, and contracts through ThunderLang's own deterministic interpreter. Answers "does the declared intent behave correctly?" No implementation required.
  • Target mode (--target typescript|python|...): run the same tests against generated or hand-written implementations. Answers "does the actual implementation conform to the declared intent?" --all-targets compares every target side by side, so a Python implementation that violates a contract is visible immediately.

Verification is not one thing

Not every guarantee is a simple assertion. ThunderLang classifies how a claim is verified:

Type Used for
Assertion Inputs, outputs, rules, state
Static analysis Types, dependencies, architecture, security
Runtime evidence Logs, events, traces, database effects
External attestation Scanner, CI system, deployment platform
Human approval Architecture, legal, compliance, product judgment
Formal proof Properties that can be mathematically established
guarantee EveryInvoiceIsAuditable
  verify by evidence
    database contains AuditRecord
    emitted InvoiceCreated

Honest verification states

PASS FAIL UNVERIFIED INCONCLUSIVE STALE BLOCKED NOT_APPLICABLE

UNVERIFIED must never silently become PASS. (thunder prove already enforces this: a guarantee or never with nothing attached is reported UNVERIFIED and never counted as proven.) Honesty about what is not yet proven is a core trust advantage.

Stable identities

Goals, requirements, guarantees, prohibitions, scenarios, and tests need stable IDs so semantic diffs, traceability, and proof survive renames and moved files.

An explicit id on a guarantee or never rule is supported today and flows through to the proof; without one, the compiler falls back to a slug of the statement.

guarantee total is never negative
  id INV-G-001
  verify total test

never expose payment token in logs
  id INV-N-004

Proof freshness

A proof is valid only for a specific combination of intent, implementation, dependencies, and environment. When any of those change, the proof becomes STALE and must never keep displaying an old green status.

Independent verification

The same agent must not write the feature, write the tests, claim they passed, approve the result, and generate the proof.

The agent may author the test. OpenThunder and ThunderLang determine whether it passed.

Agent proposes implementation
  -> ThunderLang compiler evaluates semantics
  -> independent test runner executes tests
  -> OpenThunder observes commands and repo state
  -> policy engine evaluates evidence
  -> human or automated gate approves
  -> proof record is created

Semantic coverage

Line coverage is not enough. ThunderLang tracks goal, requirement, rule, guarantee, prohibition, scenario, target, trace, and change coverage, so "12 of 15 prohibitions challenged" is meaningful in a way "82% line coverage" is not.


Build order

Build first (toward 1.0): stable IDs; deterministic decision execution (done); example tests (done); automatic guarantee/never obligations (partial, surfaced by prove); fixtures; PASS/FAIL/UNVERIFIED/STALE states (partial); JSON evidence output (done for prove/test); TypeScript target adapter; intent coverage (partial); OpenThunder Change Ledger integration; proof invalidation on change; a reference conformance suite.

Build next: scenario tests; state/event assertions; property-based testing; Python and C# adapters; mutation testing; change-impact selection via the Intent Graph; security and privacy policies; AI evaluations.

Defer: full formal theorem proving; every implementation target; distributed model checking; production runtime enforcement; a hosted test cloud; enterprise approval workflows.


The architecture this serves

Prompt
  -> ThunderLang intent
  -> contracts and test obligations
  -> implementation
  -> independent execution and observation
  -> OpenThunder Change Ledger
  -> verification evidence
  -> fresh or stale proof

ThunderLang defines the intent. ThunderLang tests the intent. OpenThunder verifies the implementation and preserves the proof.

That is what turns "Define it. Build it. Prove it." from a tagline into an enforceable engineering system.