Intent-Driven Development

Describe what you want. Your agent writes the spec and the code. NTNT checks that the two match.

IDD is built into the language. When you ask an agent for a feature, it writes a .intent file alongside the implementation, and ntnt intent check runs every scenario against the real code. The agent gets a closed feedback loop; you get a spec you can actually read.

Keep requirements close to the checks.

You write code, then tests to cover it, while the requirements live in a ticket nobody rereads. Nothing connects what was asked for to what was built. IDD makes the requirement itself the executable check.

Requirements are the tests

Features and scenarios in plain language, backed by a glossary that resolves each phrase to a concrete check. One source of truth, reviewed by you.

Code points at the spec

@implements annotations tie functions to features, so coverage is a question the toolchain can answer: ntnt intent coverage.

Agents close their own loop

The agent runs the check, reads the failures, and iterates. Intent lint catches unresolved phrases and empty scenarios before execution; review the assertions and coverage, not just the exit code.

From prompt to verified code.

Pair the intent file with its implementation, then run lint and check. A passing scenario verifies its assertions, not every property of the application.

The agent writes the intent

From your prompt, the agent creates a .intent file: features, scenarios, and a glossary that maps natural phrases to executable checks. You review the spec before any code exists.

“A user visits the home page” resolves to GET /. “The page loads” resolves to status 200. Define a phrase once; reuse it everywhere.

home.intent
## Glossary

| Term                   | Means                  |
|------------------------|------------------------|
| a user visits {path}   | GET {path}             |
| the home page          | /                      |
| the page loads         | status 200             |
| they see {text}        | body contains {text}   |

---

Feature: Home Page
  id: feature.home

  Scenario: Shows welcome message
    When a user visits the home page
    → the page loads
    → they see "Welcome"

  Scenario: Shows navigation
    When a user visits the home page
    → they see "About"
    → they see "Contact"

Lint the spec before running it

ntnt intent lint checks the glossary statically: unresolved phrases, definition cycles, and orphaned terms, with did-you-mean suggestions. Typos in a spec fail fast instead of silently skipping.

terminal
$ ntnt intent lint home.intent

✗ [unresolved_term] Home Page › Shows navigation: "they spot "Contact""
    term not found in glossary or standard vocabulary
    did you mean: 'they see {text}'

2 scenarios and 4 glossary terms checked: 1 error(s), 0 warning(s)

The agent implements

The agent writes the code and links it to the feature with an @implements annotation, so NTNT knows which code answers for which requirement.

home.tnt
import { html } from "std/http/server"

// @implements: feature.home
fn home(req) {
    return html("<h1>Welcome</h1><a href='/about'>About</a> <a href='/contact'>Contact</a>")
}

get("/", home)
listen(8080)

NTNT verifies

ntnt intent check starts the app, runs every scenario, and gives a definitive pass or fail. Checks run in strict type mode by default. The agent iterates on failures; you see the green result.

terminal
$ ntnt intent check home.tnt -vv

✓ Feature: Home Page
  ✓ Shows welcome message
      → the page loads
      → they see "Welcome"
        ✓ status: 200
        ✓ body contains "Welcome"
  ✓ Shows navigation
      → they see "About"
      → they see "Contact"
        ✓ status: 200
        ✓ body contains "About"
        ✓ body contains "Contact"

=== Summary ===
Features:   1 total, 1 passed, 0 failed
Scenarios:  2 passed, 0 failed, 0 skipped
Assertions: 5 passed, 0 failed

More than HTTP tests.

The same workflow covers plain functions, cross-cutting rules, and runtime contracts. One methodology for the whole codebase.

# utils.intent — no server needed

## Glossary

| Term                | Means                                     |
|---------------------|-------------------------------------------|
| slugifying {text}   | call: to_slug({text}), source: slug.tnt   |

---

Feature: URL Slugs
  id: feature.slugs

  Scenario: Basic conversion
    When slugifying "Hello World"
    → result is "hello-world"
    → is deterministic

  Scenario: Trims whitespace
    When slugifying "  NTNT  "
    → result is "ntnt"
# security.intent

Constraint: Security Headers
  description: "Every page sets basic security headers"
  applies_to: [feature.home, feature.api]

  → header "X-Frame-Options" exists
  → header "X-Content-Type-Options" equals "nosniff"
fn withdraw(balance: Int, amount: Int) -> Int
    requires amount > 0
    requires amount <= balance
    ensures result >= 0
{
    return balance - amount
}

// A failed contract reports the runtime values that broke it.
// In HTTP handlers: requires fails → 400, ensures fails → 500.

Functions

A glossary entry with call: and source: tests a function directly, with no server. Property checks like is deterministic call it repeatedly and compare.

Constraints

Rules that apply across features: define security headers or error policies once, and every listed feature is checked against them.

Contracts

requires and ensures document the function and enforce it at runtime. Agents read them as specs; people read them as docs.

A vocabulary that ships with the language.

These terms work in any intent file without a glossary entry. The full list lives in the IAL reference.

HTTP

  • status 200, status 4xx
  • body contains "text"
  • body not contains "text"
  • body matches "pattern"
  • body has field "key"
  • content-type is json
  • header "X" equals "Y"
  • response time < 100ms

Functions

  • result is "value"
  • starts with "prefix"
  • does not contain "text"
  • is at most 10
  • length is at most 64
  • is deterministic
  • is idempotent

CLI

  • exits successfully
  • exit code is 2
  • stdout contains "text"
  • stderr is empty

Code quality

  • code passes lint
  • no lint warnings
  • no syntax errors

The IDD command set.

Five subcommands under ntnt intent. Lint and check both support --json for CI and agents.

ntnt intent check app.tntRun every scenario against the implementation; pass or fail for each
ntnt intent lint app.intentStatically validate the glossary and scenarios before running anything
ntnt intent coverage app.tntShow which features have linked implementations
ntnt intent init app.intent -o app.tntGenerate code scaffolding from an intent file
ntnt intent studio app.intentVisual workspace with live test results as you edit

Try it on your next feature.

Install NTNT, point your agent at it with ntnt learn, and describe what you want. The spec, the code, and the proof come back together.