t27.aiРусский

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.

Open the interactive lesson →

From a spec to a bot: break the table, watch a check go red ↗

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;
}

Open the lesson's spec in the player ↗

All lessons