# 13.4 Decisions and repetition (/building-ai-workers/programming-fundamentals/decisions-and-repetition)

---
type: Document
title: "13.4 Decisions and repetition"
description: "How to find each decision and loop in agent-written code, say it as a sentence, and catch the empty value that slips through."
status: stable
order: 313.04
ksor:
  owner: team:panaversity
  audience: [ public ]
  approval:
    by: process:panaversity
    at: 2026-11-10T15:33:13Z
chapter: "13"
part: III
expert_status: builder-only
concepts: [ "13.4" ]
last_verified: 2026-10-30
generated:
  at: 2026-11-10T15:33:13Z
  by: process:panaversity
trust_tier: unverified
build_id: sha256:46fd53c5d7ab1c14f6347908d45c9fdaecda4d914439923ee878bdcecd57803e
dirty: true
ksor_version: 0.0.60
---

**In everyday life.** A mail clerk sorts the morning mail. Each envelope stamped BILL goes in the AP tray. Every other envelope goes in the general tray, even a bill that nobody stamped. The clerk repeats one small decision for every envelope. That is most of what a program does too.

**What you will learn.** How to find each decision and each repeated step, and say it as a sentence.

**Why it matters.** Each decision is a rule, and an empty value can slip through one.

A program has two moves, and the clerk makes both: decide, and repeat. This lesson takes them one at a time. Each one starts at the clerk's desk, and the notes beside the code say the clerk's sentence, line by line.

### How do I find each decision?

Look for every place where the code chooses: an `if`, a filter, or an `or`. The sections below show each one.

**At work.** Ana's Chapter 12 script makes three decisions for every invoice. It skips the invoice unless it is open. It takes the invoice's due date, or computes one from the terms. And it keeps the invoice if the due date is on or before Friday. She checks each one against the rule. The agent's first plan in [Chapter 12](../agentic-coding/permissions-and-plan.md) missed the second one.

### How does code choose between two things?

With an **`if`**. The clerk's rule is one sentence: if the stamp says BILL, the envelope goes in the AP tray, and otherwise it goes in the general tray. The code is that sentence. Predict what each prints.

```ts tab="TypeScript" tab-group="language"
const stamp = "BILL";
if (stamp === "BILL") {         // if the stamp says BILL
  console.log("AP tray");       //   it goes in the AP tray
} else {                        // otherwise
  console.log("general tray");  //   it goes in the general tray
}
```

```python tab="Python" tab-group="language"
stamp = "BILL"
if stamp == "BILL":         # if the stamp says BILL
    print("AP tray")        #   it goes in the AP tray
else:                       # otherwise
    print("general tray")   #   it goes in the general tray
```

Both print `AP tray`. With any other stamp, both print `general tray`.

Each part of the clerk's sentence has a name in code.

- **The question is the condition.** "Does the stamp say BILL?" has a yes-or-no answer. `===` in TypeScript and `==` in Python ask whether two values are equal.
- **"Otherwise" is `else`.** Its lines run when the answer is no.
- **The lines that belong together are a block.** TypeScript marks a block with braces `{ }`. Python marks it by indentation, the spaces at the start of each line, so moving a line left or right changes which block it belongs to.

### How does code do the same thing to every item?

With a **loop**. The clerk does not decide once and go home. The rule is "for each envelope in the tray, read the stamp". The clerk picks up one envelope, deals with it, and picks up the next, until the tray is empty. A `for` loop does this with a list.

Ana's tray is the list `invoices`. Each invoice is a short list of its own: its number, its status and its due date, as in [13.1](you-read-the-agent-writes.md#why-predict-before-i-run). The name `inv` is the envelope in the clerk's hand. On each pass, it holds the next invoice. Predict how many lines print, and what the first one is.

```ts tab="TypeScript" tab-group="language"
const invoices = [
  ["INV-8301", "open", "2027-03-03"],
  ["INV-8309", "paid", "2027-02-28"],
  ["INV-8305", "open", ""],
];

for (const inv of invoices) {    // for each invoice in the tray
  console.log(inv[0], inv[1]);   //   print its number and status
}
```

```python tab="Python" tab-group="language"
invoices = [
    ["INV-8301", "open", "2027-03-03"],
    ["INV-8309", "paid", "2027-02-28"],
    ["INV-8305", "open", ""],
]

for inv in invoices:           # for each invoice in the tray
    print(inv[0], inv[1])      #   print its number and status
```

Both print:

```text
INV-8301 open
INV-8309 paid
INV-8305 open
```

The tray held three invoices, so the block ran three times. No line tells this loop to stop. It stops when the list runs out. In TypeScript, each pass makes a new `inv`, so `const` still fits.

### How does code keep only some of the items?

With an `if` inside a loop, which is called a **filter**. It is the clerk's whole job in one sentence: for each envelope in the tray, if it is stamped BILL, put it in the AP tray. Ana wants only the open invoices, and `inv[1]` is the status. Predict which invoices print.

```ts tab="TypeScript" tab-group="language"
for (const inv of invoices) {    // for each invoice in the tray
  if (inv[1] === "open") {       //   if its status is open
    console.log(inv[0]);         //     print its number
  }
}
```

```python tab="Python" tab-group="language"
for inv in invoices:           # for each invoice in the tray
    if inv[1] == "open":       #   if its status is open
        print(inv[0])          #     print its number
```

Both print:

```text
INV-8301
INV-8305
```

INV-8309 is paid. Its answer was no, so nothing printed for it.

**The agent's shorter form.** Agents often write a filter in one line. In TypeScript it is `invoices.filter((inv) => inv[1] === "open")`. A dot means "this thing's own", and `(inv) => ...` means "for each `inv`, the answer is what follows the arrow". In Python it is a **list comprehension**: `[inv for inv in invoices if inv[1] == "open"]`, read as "each inv in invoices, if its status is open". When you meet one, ask the agent which items it keeps, and check its answer against yours.

### How does code ask two questions at once?

It joins two conditions with **and** or **or**. The clerk's rule could be "stamped BILL and addressed to Brightline", where both must be true. Or it could be "stamped BILL or stamped INVOICE", where either one is enough. Ana's rule needs and: an invoice is on Friday's list only if it is open and due on or before Friday. With or in that place, a paid invoice gets through.

"On or before" is a comparison of sizes. `>` asks whether a value is greater, and `<` whether it is less. `>=` and `<=` also say yes when the two are equal, so `5 > 5` is false and `5 >= 5` is true.

Only two of these marks differ between the languages.

| | TypeScript | Python |
| --- | --- | --- |
| Equal | `===` (always use this one, not `==`) | `==` |
| And, or, not | `&&`, `||`, `!` | `and`, `or`, `not` |

### What does code do with an empty value?

Whatever its rule says, and that is the trouble. The clerk's rule has a hole: a bill that nobody stamped goes in the general tray, and nobody pays it. The clerk did nothing wrong. The empty stamp slipped through the rule. Every decision in code has the same hole, so ask of each one what happens when the value is empty.

Ana's list has an empty value: INV-8305 has no due date. A careful clerk has a rule for that: "use the due date on the invoice, or, if it is blank, the one from the terms". Code says this with or. `a || b` in TypeScript, and `a or b` in Python, gives `a`, or `b` when `a` is empty. Predict both lines.

```ts tab="TypeScript" tab-group="language"
for (const inv of invoices) {
  if (inv[1] === "open") {
    const due = inv[2] || "from the terms";   // its due date, or this text if blank
    console.log(inv[0] + " " + due);
  }
}
```

```python tab="Python" tab-group="language"
for inv in invoices:
    if inv[1] == "open":
        due = inv[2] or "from the terms"   # its due date, or this text if blank
        print(inv[0] + " " + due)
```

Both print:

```text
INV-8301 2027-03-03
INV-8305 from the terms
```

In a condition, the number 0, empty text `""` and "nothing" all count as false. That is why the empty due date used the text after `||`. It is also a trap: a condition meant to ask "is there an amount?" would treat a zero-dollar credit as missing.

### How does a loop stop?

A `for` loop stops when its list runs out, because the whole tray is there to see. A **`while` loop** is for work where nobody knows the count in advance. Say the mail comes in bags, and nobody says how many. The clerk's rule is "while the bag I just opened has mail in it, sort it and fetch the next bag". An empty bag ends the job. A clerk who forgets to fetch the next bag opens the same one forever.

Ana's bags are pages of invoices, read until one comes back empty. `pages` is a list of lists, one for each page, and `pages[page]` is the one at that position. Predict the last line.

```ts tab="TypeScript" tab-group="language"
const pages = [["INV-8301", "INV-8302"], ["INV-8306"], []];
let page = 0;
while (pages[page].length > 0) {   // while this page has invoices
  console.log("page", page + 1, "has", pages[page].length);
  page += 1;                       //   fetch the next page
}
console.log("stopped at page", page + 1);
```

```python tab="Python" tab-group="language"
pages = [["INV-8301", "INV-8302"], ["INV-8306"], []]
page = 0
while len(pages[page]) > 0:   # while this page has invoices
    print("page", page + 1, "has", len(pages[page]))
    page += 1                 #   fetch the next page
print("stopped at page", page + 1)
```

Both print:

```text
page 1 has 2
page 2 has 1
stopped at page 3
```

`page += 1` adds 1 to `page`. It is the clerk fetching the next bag, the line that moves the loop toward its end. Every `while` loop needs a line like it, one that changes something its condition checks. Without one, the loop never ends. Positions count from 0, so the code prints `page + 1`.

![Figure 13.4. A while loop reading pages. Page 1 holds INV-8301 and INV-8302 and page 2 holds INV-8306, so each time the loop prints and moves on with page += 1. Page 3 is empty, so the condition is false and the loop stops. Without page += 1, it would fetch the same page again and again.](img/while-loop.png)

*Figure 13.4. A while loop. Each pass checks the condition again, and page += 1 is what moves it toward the empty page that stops it.*

### Try it

The file in `13-4` is the invoice loop, with its `if` and its or. Run it and investigate, as in [13.1](you-read-the-agent-writes.md#try-it). Then write each decision in it as a sentence in your own words, such as "keep an invoice only if its status is open".

**Modify.** Send: "Please change INV-8301's status to `Open`, with a capital O. Don't run it yet." Use your sentence to predict which invoices it lists now. Then ask the agent to run it.

**Make.** Send: "Please make a new file in 13-4 called mine, with the same invoices, that lists only the paid ones. Don't run it yet." Read its `if`, say what it will list, and then ask the agent to run it.

**What you should see.** After your change, only `INV-8305 from the terms` is listed, because `Open` is not `open`. `mine` lists `INV-8309`.

**The operating rule:** find every decision in the code and say it as a sentence, then check each sentence against the business rule, including what happens to empty values. For every `while` loop, find the line that makes it stop.

![Figure 13.5. Four invoice rows through a filter for Friday, March 5, 2027. INV-8301 is open and due March 3, so it stays. INV-8309 is paid, so it leaves at the first check. INV-8305 has no due date, so Net 30 gives March 5, and it stays. INV-8303 is due March 6, after Friday, so it leaves.](img/row-through-filter.png)

*Figure 13.5. A row through a filter. Each gate is one condition.*

**Next.** A decision you need twice belongs in one place. [13.5](functions.md) gives it a name.
