From a spec to a bot
You will learn
Why a bot starts as a table of commands and replies, and how three checks catch a bad table before any bot exists.
You do not write a bot, you describe it. spec_to_bot.t27 lists 3 commands and the reply each one gets, as data. Before anything is generated, 3 checks run on that description: every command has a reply, no two commands share a reply, and the number of routes equals the number of commands. A description that fails a check would make a wrong bot, so the table is fixed first and the bot is generated afterwards. Blank a reply in the widget and see which check turns red.
Try it
Blank one reply and name the check that turns red. In the spec frame, find the test that a missing reply breaks, and the one line you would change to plant that bug.
A bot written down as a table of commands and replies. Blank a reply or repeat one and the checks turn red before any bot exists. Nothing is sent.
specs/vibecoding/spec_to_bot.t27
// SPDX-License-Identifier: Apache-2.0
; specs/vibecoding/spec_to_bot.t27 -- lesson "From a spec to a bot" of the course Vibecoding from zero
; The one idea: you do not write the bot, you write down what it should do, as data, and check the
; description before anything is generated from it. This spec lists three commands and the reply
; each one gets; its tests are the checks a description must pass: every command has a reply, no
; two commands share a reply, and the count of routes equals the count of commands.
; Command ids: 1 start, 2 help, 3 about. Reply ids: 10, 20, 30; a reply id of 0 means "missing".
; ASCII only (L3), English only (LANG-EN).
; phi^2 + 1/phi^2 = 3 | TRINITY
module vc_spec_to_bot;
pub const BOT_NAME : str = "first-bot";
pub const COMMAND_COUNT : u8 = 3;
pub const COMMAND_IDS : [3]u8 = [1, 2, 3];
; The reply for the command in the same position of COMMAND_IDS.
pub const REPLY_IDS : [3]u8 = [10, 20, 30];
pub const ROUTE_COUNT : u8 = 3;
; A route is complete when it has a reply.
pub fn has_reply(reply: u8) -> bool {
return reply != 0;
}
test every_command_has_a_reply {
assert REPLY_IDS[0] != 0;
assert REPLY_IDS[1] != 0;
assert REPLY_IDS[2] != 0;
}
; Negative control: a description that forgets one reply (REPLY_IDS = [10, 0, 30]) turns this red.
test no_reply_is_shared_by_two_commands {
assert REPLY_IDS[0] != REPLY_IDS[1];
assert REPLY_IDS[0] != REPLY_IDS[2];
assert REPLY_IDS[1] != REPLY_IDS[2];
}
test no_command_is_listed_twice {
assert COMMAND_IDS[0] != COMMAND_IDS[1];
assert COMMAND_IDS[0] != COMMAND_IDS[2];
assert COMMAND_IDS[1] != COMMAND_IDS[2];
}
test the_routes_match_the_commands {
assert ROUTE_COUNT == COMMAND_COUNT;
assert COMMAND_IDS[COMMAND_COUNT - 1] == 3;
assert REPLY_IDS[ROUTE_COUNT - 1] == 30;
}
All lessons
Module 1 · What vibecoding is
You describe, an agent writes, you run it and check. What a program is, and five myths put to a test.
Module 2 · Your desk
An editor with an agent beside it, ten terminal commands, and reading an error message.
Module 3 · Save points with git
A commit is a save point, a branch is a safe try, and a key is a pair of which one stays private.
Module 4 · Prompting
The parts of a prompt, an example in place of an adjective, and one small step at a time.
Module 5 · The working loop
Four phases from idea to ship, the loop of ask, run, read and fix, and what to do when it is stuck.
Module 6 · A test as the judge
A test says yes or no, a planted bug shows the test works, and a diff is read before it is accepted.
Module 7 · The parts of an agent
What an agent answers with, what it can do with tools, and what it keeps between messages.
Module 8 · A first bot
A bot is a message loop, a bot comes from a spec, and a secret stays out of the repository and the prompt.
Module 9 · Your own project
A spec for your own idea, one thing measured after it runs, and an honest plan for the next month.