Tutorials

Hands-on projects that walk you through building real applications with BOA. Each tutorial has two tracks — pick the one that fits you.

Track A — Non-Developers

You describe what you want in plain English. An AI assistant (Claude, ChatGPT, Copilot, etc.) generates the entire project for you. You just run it.

Track B — AI Agents & Developers

You want to understand the architecture: how requirements decompose into blocks, how blocks wire together, and why the framework is designed this way.


All Tutorials


Tutorial 1: Tip Calculator

You’re at a restaurant with friends. You want a tool that takes the bill amount, tip percentage, and number of people — then tells you exactly how much each person pays. Let’s build it.

Beginner Node.js ~15 minutes

What you’ll learn

  • How to create a BOA project from scratch
  • How blocks encapsulate single responsibilities
  • How workflows chain blocks together with data mapping
  • How to test and validate everything with one command

What you’ll build

Two blocks and one workflow:

TipCalculator/
  project.boa ← project overview
  blocks.boa ← block registry
  src/
    DomainBlocks/
      CalculateTip/
        block.boa ← manifest + rules + tests
        index.ts ← implementation
    Primitives/
      SplitBill/
        block.boa
        index.ts
  workflows/
    tip-calculator/
      workflow.boa ← chains blocks together
      input.json ← sample input

The data flows like this:

data flowInput: { billAmount: 85.50, tipPercent: 18, people: 3 }

  CalculateTip  →  tipAmount: 15.39, totalWithTip: 100.89
        |
  SplitBill     →  perPerson: 33.63

Output: Each person pays $33.63
How this works You don’t need to write any code. You’ll describe what you want to an AI assistant, and it will generate the entire project for you. You just run the commands to see the result.

Install the BOA CLI

Open a terminal and run this command. It gives you the boa tool that runs your projects:

bashnpm install -g @boa-framework/core

Verify it worked:

bashboa --help

Create a project folder

Create a new folder and initialize it as a BOA project:

bashmkdir TipCalculator && cd TipCalculator
boa init

This sets up the basic project structure. You now have a project.boa, blocks.boa, and src/ directory ready to go.

Give your AI assistant this prompt

Open this project folder in your AI coding assistant (Claude Code, Cursor, GitHub Copilot, etc.) and give it this prompt:

“I want a tip calculator. It takes a bill amount, a tip percentage, and the number of people. It should calculate the tip, add it to the bill, then split the total evenly. Tell me how much each person pays. Round everything to 2 decimal places. Use Node.js.”

The AI will read your project’s CLAUDE.md (or equivalent instructions), understand the BOA framework, and generate all the blocks, manifests, and workflows automatically.

What the AI generates for you

Behind the scenes, the AI creates two blocks and one workflow. You don’t need to understand the code — just know that it broke your request into small, testable pieces:

  • CalculateTip — takes the bill and tip percentage, calculates the tip amount and total
  • SplitBill — takes the total and number of people, calculates per-person share
  • TipCalculator workflow — connects them: bill flows into CalculateTip, the total flows into SplitBill

Test that everything works

Run the validation command to make sure all the pieces are correct:

bashboa validate

You should see something like:

  Blocks: 2 registered
  Fixtures: 4 passed, 0 failed
  Workflows: 1 valid
  Status: OK All valid

All green. Every block tested itself automatically using the test cases the AI included.

Run it with real numbers

Create a file called input.json in your workflows folder with the numbers you want to calculate:

input.json{
  "billAmount": 85.50,
  "tipPercent": 18,
  "people": 3
}

Now run the workflow:

bashboa run workflows/tip-calculator/workflow.boa --input-file workflows/tip-calculator/input.json

Result

{
  "perPerson": 33.63
}

Each of the 3 people pays $33.63 (bill $85.50 + 18% tip = $100.89, split 3 ways).

Change the numbers and run again

Edit input.json with different values and run the same command. Try a dinner for 5 people with a 20% tip, or a coffee for 2 with 15%. The workflow handles any input.

Want to add a new feature? Just tell your AI assistant:

“Add a minimum tip rule: if the tip percentage is less than 10%, round it up to 10%.”

The AI will modify the right block, update the test cases, and revalidate — all without breaking the existing workflow.

Why does this work so well? BOA projects are designed so that AI assistants can read the project structure, understand every piece, and make safe changes. Each block is small enough for the AI to hold in context, and the contracts between blocks prevent changes from causing unexpected side effects.
This track is for you if You’re building AI agents that generate BOA projects, or you’re a developer who wants to understand the architecture. We’ll walk through every file, explain every decision, and show how the framework enforces correctness.

Step 1: Requirement Decomposition

The user said: “Calculate tips and split the bill.”

This decomposes into two distinct responsibilities:

Responsibility Layer Block Why this layer?
Apply tip percentage to a bill domain CalculateTip Business rule: how tips are calculated
Divide a number by N people primitive SplitBill Generic math — reusable anywhere

Why two blocks, not one?

A single “calculateTipAndSplit” function would work, but it violates the single responsibility principle. Splitting the bill is generic math (primitive layer) — it has no knowledge of tips. CalculateTip is a business rule (domain layer) — it knows what a “tip percentage” means. Keeping them separate means SplitBill can be reused in any project that needs to divide amounts.


Step 2: Project Scaffolding

bashmkdir TipCalculator && cd TipCalculator
boa init
boa block create CalculateTip --layer domain --runtime node
boa block create SplitBill --layer primitive --runtime node

This generates the folder structure, project.boa, blocks.boa, and stub files for each block.


Step 3: CalculateTip Block (Domain Layer)

This block encapsulates the business rule: “a tip is a percentage of the bill.”

block.boa

block.boaBLOCK CalculateTip 1.0.0
LAYER domain
RUNTIME node
ENTRY index.js
DESC Calculates tip amount and total from a bill.

INTENT User wants to calculate how much tip
  to leave on a restaurant bill.

RULE tipAmount = billAmount * (tipPercent / 100).
RULE totalWithTip = billAmount + tipAmount.
RULE Round both outputs to 2 decimal places.

TAGS tip, restaurant, billing, math

IN billAmount:number!
IN tipPercent:number!
OUT tipAmount:number
OUT totalWithTip:number

FIXTURE {"billAmount":100,"tipPercent":20}
  -> {"tipAmount":20,"totalWithTip":120}

FIXTURE {"billAmount":85.50,"tipPercent":18}
  -> {"tipAmount":15.39,"totalWithTip":100.89}

ERR ValidationError

index.ts

TypeScriptasync function main() {
  const chunks: Buffer[] = [];
  for await (const chunk of process.stdin)
    chunks.push(chunk as Buffer);
  const envelope = JSON.parse(
    Buffer.concat(chunks)
      .toString("utf-8")
  );
  const { billAmount, tipPercent } =
    envelope.input;

  if (typeof billAmount !== "number" ||
      typeof tipPercent !== "number") {
    console.log(JSON.stringify({
      success: false,
      error: {
        type: "ValidationError",
        message: "Inputs must be numbers"
      }
    }));
    return;
  }

  const tipAmount =
    Math.round(
      billAmount * (tipPercent / 100) * 100
    ) / 100;
  const totalWithTip =
    Math.round(
      (billAmount + tipAmount) * 100
    ) / 100;

  console.log(JSON.stringify({
    success: true,
    output: { tipAmount, totalWithTip }
  }));
}

main();
Key anatomy of a block.boa INTENT preserves the user’s original words — so future AI sessions know why this block exists. RULE lines are structured constraints an AI can parse and verify. FIXTURE lines are self-testing — run boa test CalculateTip@1.0.0 and the framework feeds the input through the block and compares against the expected output.

Step 4: SplitBill Block (Primitive Layer)

This block is pure math with no business context. It lives in the primitive layer because dividing a number by N is a generic operation.

block.boa

block.boaBLOCK SplitBill 1.0.0
LAYER primitive
RUNTIME node
ENTRY index.js
DESC Divides a total evenly among people.

INTENT Split a total amount equally
  among a group of people.

RULE perPerson = total / people.
RULE Round up to 2 decimal places (ceiling).
RULE people must be at least 1.

TAGS math, split, divide, billing

IN total:number!
IN people:number!
OUT perPerson:number

FIXTURE {"total":100,"people":4}
  -> {"perPerson":25}

FIXTURE {"total":100.89,"people":3}
  -> {"perPerson":33.63}

ERR ValidationError

index.ts

TypeScriptasync function main() {
  const chunks: Buffer[] = [];
  for await (const chunk of process.stdin)
    chunks.push(chunk as Buffer);
  const envelope = JSON.parse(
    Buffer.concat(chunks)
      .toString("utf-8")
  );
  const { total, people } =
    envelope.input;

  if (typeof total !== "number" ||
      typeof people !== "number" ||
      people < 1) {
    console.log(JSON.stringify({
      success: false,
      error: {
        type: "ValidationError",
        message:
          "total must be a number, people >= 1"
      }
    }));
    return;
  }

  const perPerson =
    Math.round(
      (total / people) * 100
    ) / 100;

  console.log(JSON.stringify({
    success: true,
    output: { perPerson }
  }));
}

main();

Primitive vs Domain — the key distinction

SplitBill knows nothing about tips, restaurants, or billing context. It divides a number by another number. This means it can be reused in any future project that needs to split an amount — rent splitting, expense sharing, etc. Domain blocks like CalculateTip encode business-specific rules that only make sense in a particular context.


Step 5: Register Blocks

Every block must be registered in blocks.boa so the framework can locate them at runtime:

blocks.boaCalculateTip 1.0.0 -> src/DomainBlocks/CalculateTip
SplitBill 1.0.0 -> src/Primitives/SplitBill

The format is: Name Version -> Path. The CLI uses this to resolve CalculateTip@1.0.0 to src/DomainBlocks/CalculateTip/ when executing workflows.


Step 6: Compile and Test

Compile both TypeScript files to JavaScript:

bashnpx tsc --outDir src/DomainBlocks/CalculateTip --target ES2022 --module Node16 --moduleResolution Node16 src/DomainBlocks/CalculateTip/index.ts

npx tsc --outDir src/Primitives/SplitBill --target ES2022 --module Node16 --moduleResolution Node16 src/Primitives/SplitBill/index.ts

Test each block against its fixtures:

bashboa test CalculateTip@1.0.0
boa test SplitBill@1.0.0

Expected output:

  CalculateTip@1.0.0
    Fixture 1: PASS
    Fixture 2: PASS
  2 fixtures passed, 0 failed

  SplitBill@1.0.0
    Fixture 1: PASS
    Fixture 2: PASS
  2 fixtures passed, 0 failed
Self-testing blocks The FIXTURE declarations in block.boa are the tests. No separate test files, no test framework, no configuration. The framework reads the fixtures, feeds the input through the block, and compares the output. If you change the block, the fixtures catch regressions immediately.

Step 7: Wire the Workflow

Create workflows/tip-calculator/workflow.boa to chain the two blocks:

workflow.boaWORKFLOW TipCalculator 1.0.0
DESC Calculate tip and split the bill among people.

STEP calc = CalculateTip@1.0.0
  MAP billAmount <- _initial.billAmount
  MAP tipPercent <- _initial.tipPercent

STEP split = SplitBill@1.0.0
  MAP total <- calc.totalWithTip
  MAP people <- _initial.people

How data flows

Expression Source Destination
_initial.billAmount Workflow input JSON CalculateTip's billAmount field
_initial.tipPercent Workflow input JSON CalculateTip's tipPercent field
calc.totalWithTip CalculateTip's output SplitBill's total field
_initial.people Workflow input JSON SplitBill's people field

Workflows contain zero logic

The workflow file is purely declarative — it only describes which blocks run and how data passes between them. There are no if-statements, no loops, no functions. This is by design: workflows are the wiring diagram, blocks are the logic. An AI agent can read this file and instantly understand the entire data flow without parsing any code.


Step 8: Update project.boa

The project overview file gives any AI (or human) a complete mental model of the project in one read:

project.boaPROJECT TipCalculator 1.0.0
DESC Calculates tips and splits bills among people.

DOMAIN Billing
  CalculateTip: Computes tip amount and total with tip
  SplitBill: Divides a total evenly among people

FLOW TipCalculator: CalculateTip -> SplitBill
Read project.boa first This is the most important file in any BOA project. When an AI agent opens a project, it reads project.boa to get the full mental model — every domain, every block, every workflow — in a few lines. No need to crawl directories or parse dozens of files.

Step 9: Run and Validate

Create workflows/tip-calculator/input.json:

input.json{
  "billAmount": 85.50,
  "tipPercent": 18,
  "people": 3
}

Run the workflow:

bashboa run workflows/tip-calculator/workflow.boa --input-file workflows/tip-calculator/input.json

Expected output:

{
  "perPerson": 33.63
}

Validate the entire project:

bashboa validate
  Blocks: 2 registered
  Fixtures: 4 passed, 0 failed
  Workflows: 1 valid
  Status: OK All valid

Understanding the Universal Runtime Protocol (URP)

Every backend block communicates via a simple JSON contract over STDIN/STDOUT:

What the framework sends (STDIN)

JSON{
  "block": "CalculateTip",
  "version": "1.0.0",
  "input": {
    "billAmount": 85.50,
    "tipPercent": 18
  }
}

What the block returns (STDOUT)

JSON{
  "success": true,
  "output": {
    "tipAmount": 15.39,
    "totalWithTip": 100.89
  }
}

This is why BOA is polyglot — any language that can read STDIN and write STDOUT JSON works. The framework spawns the block as a child process, pipes the envelope in, and reads the result out. Node.js, Python, Go, Rust — it all works the same way.


Why This Architecture Matters for AI

BOA exists to solve the “large codebase + AI context limit” problem. Here’s why each design decision helps:

Design Decision AI Benefit
Blocks are < 200 lines Any single block fits entirely in an AI’s context window
Explicit IN/OUT contracts AI knows exactly what a block accepts and returns without reading code
FIXTURE declarations AI can verify its changes are correct by running boa test
INTENT keyword Future AI sessions know why a block was created, not just what it does
RULE keyword Business rules are structured text, not buried in code comments
Version pinning Workflows reference exact versions, preventing accidental breakage
project.boa overview One file gives the AI the complete mental model of the entire project
Isolation between blocks Modifying one block cannot break another — low blast radius