The product is not a language. The product is not an IDE. The product is the feedback loop: "does the machine understand what I want?"
As AI agents get better at writing code, the bottleneck shifts from implementation to intent. The developer who can precisely describe what they want gets correct software. The one who can't gets plausible-looking bugs.
The Specification IDE tightens that loop from hours to seconds.
Today: Write prompt → Get code → Read code → Find misunderstanding → Re-prompt
(minutes) (seconds) (minutes) (frustrating) (repeat)
Spec IDE: Write spec → Consistency check → Generate → Verify → Refine
(seconds) (instant) (seconds) (seconds) (seconds)
- The Core Idea
- The Spec Language
- Architecture
- Running Locally
- UI/UX Walkthrough
- Project Structure
- Build Status
- Design Documents
Every successful developer tool won by tightening a feedback loop:
| Tool | Loop it tightened |
|---|---|
| Compilers | "Did I make a syntax error?" |
| REPLs | "What does this evaluate to?" |
| Hot reload | "What does this look like?" |
| TDD | "Does this work?" |
| Type systems | "Is this structurally sound?" |
| Spec IDE | "Does the machine understand what I want?" |
That last loop doesn't exist as a tool today. It's done manually by reading generated code. That's the opportunity.
At a whiteboard, skilled engineers use five modes simultaneously:
- Stories - "A user adds items, clicks buy, gets charged, sees a confirmation"
- Rules - "A user can never be charged twice for the same order"
- Processes - "An order goes from draft to confirmed to shipped to delivered"
- Boundaries - "The payment service handles charging; we assume it's up 99.9%"
- Trade-offs - "Speed matters more than perfect consistency for cart display"
No existing tool captures all five. TLA+ captures rules but is unreadable. Gherkin captures stories but can't express invariants. Natural language captures everything but is ambiguous.
The Specification IDE provides five facets - each natural for its mode of thought - all compiling to a single verifiable internal model.
A .spec file is organized by feature. Each feature mixes five facet types as needed:
entities:
User:
id: UserId
name: String
logged_in: Boolean
Cart:
owner: User
items: List<CartItem>
displayed_total: Money
Order:
id: OrderId
user: User
items: List<OrderItem>
total: Money
status: OrderStatus
payment: Payment?
OrderStatus: enum(draft, confirmed, processing, shipped, delivered, cancelled, returned)
Payment:
id: PaymentId
order: Order
amount: Money
status: PaymentStatus
PaymentStatus: enum(pending, charged, failed, refunded)
feature "checkout":
scenario "successful purchase":
given:
user.logged_in
cart.has_items
payment_method.valid
when: user.clicks("buy")
then:
payment.charge(cart.total)
order.create(items: cart.items, status: "confirmed")
cart.clear()
example:
input:
user: { id: "u1", name: "Alice" }
cart: { items: [{ name: "Book", price: 12.99 }, { name: "Pen", price: 5.00 }] }
output:
payment: { amount: 17.99, status: "charged" }
order: { total: 17.99, status: "confirmed" }
cart: { items: [] }
scenario "payment fails":
given:
user.logged_in
cart.has_items
when: user.clicks("buy")
then:
payment.charge(cart.total) -> fails
cart.unchanged
invariant "no double charge":
for any order:
count(order.payments) <= 1
severity: critical
on violation: "refund excess and alert ops"
invariant "cart consistency":
cart.displayed_total == cart.items_total
severity: high
flow "order lifecycle":
states: draft, confirmed, processing, shipped, delivered, cancelled, returned
draft -> confirmed
when: payment.succeeds
confirmed -> processing
when: warehouse.accepts
confirmed -> cancelled
when: user.requests_cancellation
allowed: "within 24 hours of confirmation"
processing -> shipped
when: warehouse.dispatches
shipped -> delivered
when: carrier.confirms_delivery
shipped -> returned
when: user.initiates_return
allowed: "within 30 days of delivery"
terminal: delivered, cancelled, returned
on stuck in processing > "48 hours":
"alert ops team"
boundary "payment gateway":
role: "external service"
operations:
charge(amount: Money, method: PaymentMethod) -> PaymentResult
refund(payment_id: PaymentId) -> RefundResult
guarantees:
"charge is idempotent for same order and amount pair"
"refund succeeds for any charged payment within 90 days"
assumes:
"available 99.9 percent of the time"
"responds within 5 seconds"
when unavailable:
"queue charge for retry with exponential backoff"
"show user payment processing state"
when slow:
"show progress indicator after 3 seconds"
"timeout at 15 seconds"
priorities "checkout experience":
1. correctness: "never charge without recording order" [non-negotiable]
2. durability: "never lose a confirmed order" [non-negotiable]
3. availability: "checkout works if recommendation service is down" [strong]
4. latency: "checkout completes under 3 seconds p95" [target]
5. consistency: "cart display may lag by up to 5 seconds" [acceptable]| Facet | Purpose | What it generates |
|---|---|---|
| Scenarios | Concrete stories with example data | Unit tests, integration tests, agent few-shot examples |
| Invariants | Cross-cutting rules that must always hold | Runtime assertions, monitoring rules, alert conditions |
| Flows | State machine lifecycles | State machine validators, stuck-state detectors, lifecycle diagrams |
| Boundaries | Contracts with external/internal services | Circuit breakers, retry policies, health checks, fallback code |
| Priorities | Explicit trade-offs between competing concerns | Performance budgets, SLO definitions, degradation logic |
One source of truth. Everything derived. Change the spec, everything updates.
- Scenarios alone cause combinatorial explosion for state machines (one per transition path)
- Invariants are cross-cutting - they don't belong in any single scenario
- Flows are the natural formalism for lifecycles that scenarios can't express compactly
- Boundaries make integration contracts explicit - the #1 cause of production failures is mismatched assumptions between components
- Priorities capture trade-offs that live nowhere in code but determine every architectural decision
Feature Specs → Parser → AST → Unifier → BCG (Behavioral Contract Graph)
|
+-----------+-----------+-----------+
| | | |
Consistency Agent Test System
Engine Prompt Generator Map
| | | |
Warnings & Generated Auto Flow
Gap Reports Code Tests Diagram
|
Verifier → Pass: Ship it
|
Clarification
Questions
The BCG is the heart. All five facets compile into one directed graph:
Scenarios → Paths through the graph (with concrete data on edges)
Flows → Subgraphs (named state machines)
Invariants → Global annotations (properties every reachable node must satisfy)
Boundaries → Interface edges (assume/guarantee labels)
Priorities → Weights on annotations (verification strictness)
This is what enables cross-facet checking:
- A scenario must trace a valid path through a flow
- No scenario's outcome may violate an invariant
- Flow transitions depending on external services must reference a boundary
- Priority levels determine how strictly each invariant is verified
Runs on the spec itself, instantly, before any code is generated. It's the type-checker for intent.
What it catches:
ERROR Dead-end state "stuck_state" has no outgoing transitions
WARNING State "orphan" is unreachable from initial state
WARNING Scenario "refund flow" doesn't match any flow transition
INFO Transition confirmed->shipped has no scenario coverage
INFO 2 of 6 transitions covered by scenarios (33%)
Every problem caught here would have been a bug in generated code.
BCG → Serializer → Structured Prompt → LLM Provider → Generated Code → Verifier
The BCG serializer produces a deterministic, section-organized prompt:
## ENTITIES- data model## FLOW- state machines with transitions## SCENARIOS- behavior with concrete examples## INVARIANTS- rules the code must maintain## BOUNDARIES- external service contracts## PRIORITIES- trade-off ordering## IMPLEMENTATION CONTEXT- target language, framework
Three LLM providers: Anthropic (Claude), OpenAI, Ollama (local). All via raw fetch() - no SDK dependencies.
Static verification checks the generated code against the BCG: balanced syntax, entity name presence, flow state presence, confidence scoring.
git clone <repo-url>
cd specification-ide
pnpm install
pnpm buildpnpm test # all 108 tests across 4 packagesOption A: VS Code debugger (recommended)
- Open the project in VS Code
- Press F5 (or Run > Start Debugging)
- A new "Extension Development Host" window opens with the
examples/folder
Option B: Manual
# Build everything
pnpm build
# Then in VS Code:
# 1. Open Command Palette (Cmd+Shift+P)
# 2. "Developer: Install Extension from Location..."
# 3. Select packages/spec-vscode/In VS Code settings (Cmd+,), search for spec-ide:
| Setting | Default | Description |
|---|---|---|
spec-ide.codegen.provider |
anthropic |
anthropic, openai, or ollama |
spec-ide.codegen.apiKey |
- | API key (or set ANTHROPIC_API_KEY / OPENAI_API_KEY env var) |
spec-ide.codegen.model |
(provider default) | Model identifier |
spec-ide.codegen.language |
TypeScript |
Target programming language |
spec-ide.codegen.framework |
- | e.g. Express, FastAPI, Django |
spec-ide.codegen.ollamaUrl |
http://localhost:11434 |
Ollama server URL |
The extension activates on any .spec file. Opening checkout.spec:
Keywords (feature, flow, scenario, invariant, boundary, priorities), strings, numbers, entity names (PascalCase), transition arrows (->), priority levels ([non-negotiable]) - all colored distinctly.
As you type, the full pipeline runs (300ms debounce). Squiggly underlines appear:
- Red (error): dead-end states, parse errors
- Yellow (warning): unreachable states, orphan scenarios, unknown entity references
- Blue (info): uncovered transitions, coverage statistics
Add a state stuck_state to a flow without any outgoing transition - a red underline appears instantly.
Context-aware completions:
| Context | What you get |
|---|---|
| Top level (col 0) | feature, entities with snippet templates |
| Inside a feature | scenario, flow, invariant, boundary, priorities with full scaffolds |
After severity: |
critical, high, medium, low |
Inside [ |
non-negotiable, strong, target, acceptable |
| Anywhere | Entity names (User, Cart, Order...) and state names (draft, confirmed...) from the BCG |
- Hover over
User→ User (entity) with all fields and their types - Hover over
OrderStatus→ OrderStatus (enum) with values listed - Hover over
draft→ draft (initial) - Flow:order lifecycle - Hover over
delivered→ delivered (terminal) - Flow:order lifecycle
Cmd+ClickonUseranywhere → jumps to the entity declarationCmd+Clickondraft→ jumps to the flow definition- Works cross-file: entities in
entities.spec, flows incheckout.spec- navigation works between them
Click the Graph icon in the editor title bar (or run Spec: Show Flow Graph from command palette):
[draft] ──> [confirmed] ──> [processing] ──> [shipped] ──> [delivered]
| |
v v
[cancelled] [returned]
- SVG rendered in a webview panel beside the editor
- BFS layered layout
- Initial states in green, terminal states in red/orange
- Live-updates as you edit the spec
Click the Sparkle icon in the editor title bar (or Spec: Generate Code):
- Progress notification: "Generating code from specification..."
- BCG is serialized to a structured prompt and sent to your configured LLM
- Generated code opens in a new editor tab beside your spec
- Result notification: "Code generated (85% confidence, model: claude-sonnet-4-5-20250929)"
Split specs across files for larger systems:
project/
entities.spec <- shared entity declarations
checkout.spec <- checkout feature
payments.spec <- payment boundary and scenarios
shipping.spec <- shipping flows
All .spec files are discovered automatically via WorkspaceManager. The BCG is built from the unified merge of all files. Cross-file references resolve correctly - no false "unknown entity" warnings. Diagnostics are attributed to the correct source file.
specification-ide/
packages/
spec-lang/ @spec-ide/lang Lexer, parser, AST definitions
spec-bcg/ @spec-ide/bcg BCG types, AST->BCG builder, expression utils
spec-check/ @spec-ide/check Consistency rules, pipeline, merge, workspace
spec-codegen/ @spec-ide/codegen Serializer, LLM providers, verification
spec-vscode/ spec-ide VS Code extension (syntax, diagnostics, completions,
hover, go-to-def, flow graph, codegen command)
examples/
checkout.spec Reference spec for an e-commerce checkout
docs/
SPEC-IDE.md Master vision document
LANGUAGE.md Spec language design
ARCHITECTURE.md BCG, consistency engine, verification loop
BUILD.md Engineering blueprint, tech choices
spec-lang (no deps - parser, AST)
|
spec-bcg (depends on spec-lang - graph builder)
|
spec-check (depends on spec-lang, spec-bcg - consistency rules)
|
spec-codegen (depends on spec-lang, spec-bcg - LLM codegen)
|
spec-vscode (depends on spec-check, spec-codegen - IDE integration)
- Language: TypeScript,
"type": "module"throughout - Build: pnpm workspaces + Turborepo
- Parser: Chevrotain (TypeScript-native, excellent error recovery)
- Tests: Vitest
- Extension bundler: esbuild (CJS output,
vscodeexternal) - LLM integration: Raw
fetch()with AbortController timeouts - no SDK dependencies
| Package | Tests | Status |
|---|---|---|
@spec-ide/lang |
18 | Lexer, parser, AST |
@spec-ide/bcg |
15 | Graph types, builder, expression utils, scenario matching |
@spec-ide/check |
33 | 5 rules, pipeline, AST merge, workspace pipeline |
@spec-ide/codegen |
42 | Serializer, 3 providers (timeout + validation), verify, generate |
spec-ide (vscode) |
- | Extension bundle: 447KB |
| Total | 108 | All passing |
| Rule | Severity | What it catches |
|---|---|---|
flow-completeness |
error | Non-terminal states with no outgoing transitions |
flow-reachability |
warning | States unreachable from initial state |
reference-integrity |
warning | References to undeclared entities |
scenario-flow-alignment |
warning/info | Orphan scenarios, uncovered transitions |
coverage-report |
info | Node and edge coverage statistics |
| Document | Contents |
|---|---|
| SPEC-IDE.md | Master vision - core thesis, three modes of work, what the IDE generates |
| LANGUAGE.md | The specification language - five facets, three formalization levels, design rationale |
| ARCHITECTURE.md | BCG internals, consistency engine, verification loop, IDE layout |
| BUILD.md | Engineering blueprint - tech choices, repo structure, week-by-week plan |
From the design docs:
Claiming "never write code again" is dishonest. Performance optimizations, platform workarounds, library-specific integration - these require human judgment at the code level. The honest pitch: write code only for the parts that require human judgment.
The framework doesn't pretend specs replace code. The spec owns behavioral truth. The code owns implementation truth. When they conflict, the IDE flags it.
TBD