Introduction to AI SDD
AI SDD stands for Artificial Intelligence Spec-Driven Development β a methodology designed to help developers take full advantage of AI while keeping complete control over their projects.
The full AI SDD methodology lives at i-smile.one. All the code for the demo ChessGame project is available on GitHub: https://github.com/denis-papin/chessgame.
Modern large language models are remarkably capable β often producing code faster and more effectively than humans. AI SDD is about leveraging that power without becoming dependent on AI-generated code we no longer understand.
In a nutshell:
- Modernize without losing control. Move fast with AI while keeping full ownership of the system.
- Tame complexity. Uncontrolled AI produces code that works today but is impossible to maintain tomorrow; AI SDD exists to prevent exactly that.
- Human-in-the-loop at every step. Every artifact AI produces is reviewed, validated, and folded back into the project's knowledge base.
- Four pillars. Accelerated development, human-controlled architecture, valuable legacy assets, and living knowledge.
- Specs are the source of truth. Define a precise context, controlled workflows, and feedback loops; let AI do the implementation while humans own validation, architecture, and direction.
- Preserve what has always mattered. Code reviews, architectural consistency, maintainability, and a deep understanding of the system β we just stop writing most of the code by hand.
The future is not AI replacing developers β it's AI amplifying experienced engineering teams.
What we're building: a ChessGame
From a software perspective, a chess game is an app that lets a human play chess against a computer. Our opponent is Stockfish, one of the strongest chess engines available today.
chessgame) talks to the
fisher-server back end over REST/JSON; the back end drives the Stockfish engine over UCI. Each
move flows to the engine; the engine replies β repeat until the game ends.At first glance it seems simple: a chessboard on a web page, the user moves pieces with the mouse, the app sends the position to Stockfish, waits for the reply, and shows the engine's move. But chess is full of rules and edge cases β castling, en passant, promotion, check, stalemate, draws β and that is exactly what makes the project interesting.
Defining the architecture
At the root of the project we place an _ai folder that holds all the knowledge of
the application. In its global subfolder lives the architecture design document.
Here is that document, fetched live from GitHub so it always reflects the current file rather than a screenshot that can drift. Scroll the panel to read it, or open the full file below.
Loading the architecture document from GitHubβ¦
An architecture document doesn't need to be complete or perfect on day one β architecture evolves as the project does. What matters is a solid starting point and a clear direction. We identified three components:
- Chess Game (frontend) β everything in the browser: the board, the pieces, interaction, and the current game state. Built with TypeScript, Vite, and Node.js.
- fisher-server (backend) β a pun on Fischer the chess champion and fish for Stockfish. It keeps game state (move history, positions, captured pieces, game id) and talks to the engine. Built in Rust.
- Stockfish (engine) β an external component reached over the UCI protocol. We need it, but we don't worry about its internals.
The frontend is itself organized in layers, each with a clear boundary:
- Infrastructure β API calls and external communication.
- Domain β local business logic and data processing.
- Events β mouse clicks, drag-and-drop, user interaction.
- Unit Tests β automated testing.
Feature F0001: start-a-game
We start every feature on its own Git branch:
git checkout -b feat/F0001-start-a-game
The goal of F0001 is to display a responsive chessboard in the browser, complete with standard coordinates along the edges. When a game starts, the board is populated from data the backend returns. In standard mode the pieces appear in their traditional positions; in random mode (for testing) the positions are randomized while the piece count is fixed.
F0001 β not an implementation.
The full prompt sequence
The work used the guide-spec-definition-md skill together with the feature name. No code was
requested yet β only the spec and its tests. Here is the complete sequence of all 10 prompts, exactly as
they were issued, each followed by a review session to refine the documents.
Click any title bar to expand that prompt.
/guide-spec-definition-md F0001-start-a-game The feature is to display a web page showing a chessboard with pieces.
The number of pieces is between 2 and 16 for White, and the same for Black.
In random mode (dev), the pieces are spread randomly across the board.
In a standard chess game, the pieces are placed on their standard starting squares.
The board will be built using regular DOM components. It must be responsive, and it will display the standard A-H and 1-8 coordinates on the edges.
The frontend will use the following API from the feature server:
GET /start-game
This API will return:
* the UUID of the current game
* the position of each piece in FEN format/guide-integration-test-rust-md F0001
Give me frontend unit tests that I can run locally to validate the frontend code.
Give me backend integration tests that I can use to test the APIs./guide-spec-definition-md F0001
I do not want to use FEN notation. Instead, I want to define my own JSON model called **Overview**.
Example:
[
["r", "n", "b", "q", "k", "b", "n", "r"], // rank 8
["p", "p", "p", "p", "p", "p", "p", "p"], // rank 7
...
]
Additional game state information:
{
"black": "short castle",
"white": "long castle"
}
I want all algorithms to be based on these representation models rather than FEN notation./guide-integration-test-rust-md F0001
Adjust the existing unit tests and integration tests to use the new `Overview` representation model instead of FEN notation wherever applicable.you wrote : TC-UT-F0001-005 β render produces 64 squares and pieces that match the Overview
Given: a jsdom document with an empty <div id="board">, and buildBoard(STANDARD).
But I think "board" should not be empty when the rendering of the 32 pieces starts. Can you fix the F0001 documents?/guide-spec-definition-md In the specs, you never name the key routine, especially the one you are going to unit test.
You could draw a sequence diagram to explain the dom events, local routines and the api calls.From the spec F0001, explain to me how the chessboard is created/guide-spec-definition-md please, integrate the explanation above into the F0001 spec, especially because it contains routine names.
Check if we can also remove some other sections ?/guide-spec-definition-md For mode=random, is it clear that the number of pieces is not random, only the positions are random?
The number of pieces is set on the web page and passed through to the restful service.Yes, there is a single number (for instance 4), passed to the back end service in order to fix the number of pieces to 4 black and 4 white (including mandatory kings).
Also update the unit tests definitions.Overview model is precisely the kind of design decision
that stays with the developer.
Specs become the source of truth
The result is a set of well-structured documents inside the
F0001-start-a-game folder.
- F0001.md β human-readable requirements, expected outputs, sequence diagrams, and the interactions between frontend modules.
- IT-F0001.md β integration tests run directly against the
fisher-server. For example,TC-IT-F0001-001verifies that/start-gamereturns the standard starting position. - unit_test_F0001.md β unit tests for the frontend TypeScript routines, each defined with Given / Input / When / Then sections.
Rather than a screenshot, here is F0001.md itself, fetched live from the
feat/F0001-start-a-game branch. Scroll the panel to read it β its internal sequence diagram
is rendered from the document's own Mermaid source. Open the full file below.
Loading the F0001 specification from GitHubβ¦
And the tests, written the same way. First IT-F0001.md β the integration-test
spec with cases such as TC-IT-F0001-001, run directly against the fisher-server:
Loading the IT-F0001 integration-test spec from GitHubβ¦
And unit_test_F0001.md β the frontend unit-test spec for the rendering routines,
likewise fetched live from GitHub:
Loading the unit-test spec from GitHubβ¦
That has a powerful consequence: you cannot, for example, modify a Rust integration test just to make it pass without also updating its specification and the business rules it enforces. The methodology forces the spec, the tests, and the code to stay in agreement.
Now we generate the code
With the specs and tests locked, a single prompt drives the implementation β and the tests are the referee.
/implement-code Implement feature F0001 and all associated tests as specified.
Run the complete test suite and use the tests as the source of truth
for validating the implementation.
Do not modify the test definitions or the business specification. If you
encounter any contradiction, ambiguity, missing requirement, or blocking
issue, stop and report the problem in the chat, explaining the conflict
and the available options.Once the code was generated, two commands brought the servers up β the Vite frontend from the
chessgame project and the Rust backend from fisher-server:
# frontend (chessgame)
npm run dev
# backend (fisher-server)
cargo run
fisher-server
logs start-game requests in real time.And then β the payoff. A fully rendered, responsive chessboard, served by the Vite frontend and driven by
the Rust backend, exactly matching the Overview model the spec defined.
/start-game.
The generated files, on GitHub
Everything F0001 produced β the spec and its tests β is Markdown living beside the code in the
feat/F0001-start-a-game branch, each generated the AI SDD way and readable in full.
When the spec catches a bug
Right after the first code generation, every test was green β but a real problem was hiding in random mode: the board could start with a forbidden set of pieces.
The problem
The piece count was correct, but the composition wasn't constrained. The random generator drew piece types with replacement, so a starting position could be illegal β for example more than one queen, more than two rooks, knights or bishops, or a pair of bishops sitting on the same square colour. None of that can happen in a real chess army.
The AI SDD way to fix it
The fix did not start in the code. It started in the spec. A single prompt added the missing
constraints to F0001 and then regenerated the implementation:
/guide-spec-definition-md F0001
in random mode, we want to enforce some constraints for the piece
choice by side color
* A single king
* max 2 bishops, both on a different square color
* max 2 knights
* max 2 rooks
* max 1 queen
* max 8 pawns
/implement-code F0001Those constraints became business rules and, crucially, the corresponding tests were written before regenerating the implementation:
1K + 1Q + 2R + 2B + 2N + 8P = 16. Exactly the kind of detail a human settles when reviewing the
spec before code is generated.
- Rule B-10 β material caps. Per colour: queens β€ 1, rooks β€ 2, bishops β€ 2, knights β€ 2, pawns β€ 8.
Verified by
TC-IT-F0001-012"random army respects the per-colour material caps." - Rule B-11 β the bishop pair. When a colour has two bishops, they must sit on
opposite-coloured squares. Verified by
TC-IT-F0001-013. - Bounds check.
TC-IT-F0001-011rejects out-of-range counts βpieces=1(below the minimum of 2),pieces=17(above the maximum of 16), and non-integer values.
IT-F0001.md β the per-colour material caps
(B-10) and the opposite-colour bishop pair (B-11).The bug is fixed
With the new rules in the spec and their tests in place, the implementation was regenerated β and the illegal starts are gone. Every random army now respects the per-colour caps and the opposite-coloured bishop pair: at most one king, one queen, two rooks, two knights, two bishops and eight pawns per side.
TC-IT-F0001-011/012/013 pass, and the board obeys the rules.