# Case study — a content factory

> **What you will learn**
> How to join everything so far into **a production pipeline that runs
> automatically every day.** Follow the structure as-is.

## What you are building

> **Every morning, one short video on a set topic is finished and waiting.**

The only human job is **checking it at the end and publishing.**

```mermaid
graph TD
  A["1 Collect<br/>terminal"] --> B["2 Script<br/>task"]
  B --> C["3 Audio<br/>terminal + model API"]
  B --> D["4 Images<br/>terminal + model API"]
  C --> E["5 Render<br/>terminal"]
  D --> E
  E --> F["6 Check<br/>task"]
  F --> G["A person reviews and publishes"]
```

That 3 and 4 sit side by side matters. **Once the script exists, audio and
images have no reason to wait for each other** — the
[independence of micro-sprints](/guide/ha-micro-sprint) used directly.

## Split into two folders

```
content-source/          ← collection, script, assets
  out/
    01-collected.json
    02-script.md
    03-audio/
    04-images/

content-render/          ← the video assembly project
  src/
  out/
    final.mp4
```

**Why split**: because
[the working directory is the area of operation](/guide/ht-working-directory).
The render project has code and runs builds, so its character differs. Split, you
can also give them different autonomy.

| Folder | Autonomy |
|---|---|
| Source | auto-approve file edits |
| Render | plan only → raise after verifying |

## Stage by stage

### 1. Collect — `terminal`

```
Daily 05:30  ·  kind: terminal
```

Fetch material from a fixed location and save it as `out/01-collected.json`.

**Why not `task`**: there is no judgement. It fetches from a fixed address and
saves in a fixed format, so
[a command is cheap and deterministic](/guide/ht-pipelines).

### 2. Script — `task`

```
Daily 06:00  ·  kind: task
```

```
[Goal]
Read out/01-collected.json and write a 60-second video script,
saving it as out/02-script.md.

[Scope]
- Do not touch anything outside out/
- If 01-collected.json is missing or empty, do nothing and report it

[Done condition]
- One scene per paragraph, blank line between paragraphs
- End each paragraph with [image: ...] describing that scene
- 400–500 words total

[Context]
- This script is read aloud as-is. It has to sound natural spoken
- Write numbers the way they are spoken
```

That is [the four parts of a good instruction](/guide/ai-intent-context) applied
directly.

**"If missing, do nothing and report it" is the key line.** If collection failed
and the script stage runs anyway, **it invents a plausible script out of
nothing.**

### 3. Audio — `terminal` + model API

```
Daily 06:20  ·  kind: terminal
```

Split the script **by paragraph** and call
[text to speech](/guide/ht-voice-image).

```
out/03-audio/
  001.wav
  002.wav
  ...
```

Two things are mandatory.

```
□ skip numbers that already exist
□ record which paragraphs failed
```

**Splitting by paragraph** exists so a failure means regenerating that paragraph
only.

### 4. Images — `terminal` + model API

```
Daily 06:20  ·  kind: terminal
```

Extract the `[image: ...]` markers from the script and call
[image generation](/guide/ht-voice-image).

```
out/04-images/
  001.png
  002.png
```

**Keep the prompts in a file.** Apply style fields (palette, composition rules)
across all of them so the tone does not drift between episodes.

### 5. Render — `terminal`

```
Daily 06:50  ·  kind: terminal
```

Take the audio and images and assemble the video. One command in the render
project.

**Why this is `terminal` is obvious** — a build command has no judgement and must
produce the same result every time.

### 6. Check — `task`

```
Daily 07:20  ·  kind: task
```

```
[Goal]
Check today's output and summarise it.

[What to check]
- Does the file count in out/03-audio/ match the paragraph count
- Does the file count in out/04-images/ match the number of [image:] markers
- Does final.mp4 exist and run 50–70 seconds
- Were there failures in any stage's log

[Done condition]
- If nothing is wrong, report "OK" and the video length only
- If something is wrong, state which stage and what, specifically

[What not to do]
- Do not fix anything you find. This ends at reporting
```

**The last line matters.** Once the checking stage starts fixing things, you can
no longer tell what the original result was.

## The judgements in this design

### Why a person is at the end

Publishing is **irreversible.** The criterion from
[guardrails](/guide/ha-guardrails) applies directly — reversible means automatic,
irreversible means a person.

```
collect · script · audio · images · render  → reversible  → automatic
publish                                      → irreversible → a person
```

### Why every stage writes a file

```
□ you can rerun from the failed stage
□ you can compare against yesterday
□ you can regenerate only the parts you dislike
```

### Why only two `task` stages

Of six stages, **only script and check need judgement.** The rest are fixed
commands. Since
[most of the cost comes from `task`](/guide/ht-pipelines), this split *is* the
running cost.

## The first two weeks

**Do not automate it all at once.**

| Period | What you do |
|---|---|
| Days 1–3 | run each stage by hand once. See where it sticks |
| Days 4–7 | put it on a schedule but **check the result daily by eye** |
| Week 2 | raise autonomy and read only the check stage's report |
| After | intervene only when a failure alert arrives |

**Skip days 1–3** and you watch six stages run for the first time
simultaneously, with no way to tell which one is the problem.

## Common failures

| Symptom | Cause | Response |
|---|---|---|
| An empty script every day | collection fails but the script stage keeps running | precondition check in the instruction |
| Audio and image counts disagree | failures skipped and it carried on | compare counts in the check stage |
| Tone differs between episodes | image style is not shared | common fields in the prompts |
| Cost higher than expected | judgement-free stages as `task` | move them to `terminal` |
| Nothing for days | the machine was off | [background run or startup item](/guide/ht-first-run) |

**The last is surprisingly common.** A closed laptop runs no schedules.

## Reshaping it

Swap the pieces and the same structure becomes a different pipeline.

| Swap | You get |
|---|---|
| Collect → internal logs, render → a document | a daily operations report |
| Collect → support tickets, render → a table | a weekly customer issue summary |
| Collect → competitor pages, render → a comparison | price monitoring |
| Collect → meeting recordings, using speech-to-text | meeting notes automation |

**The structure is identical** — collect → judge → process → assemble → check →
a person.

---

## Check yourself

**1. Why are only two of the six stages `task`?**

<details>
<summary>Answer</summary>

**Because only the script and check stages need judgement.** The rest are fixed
commands, where `terminal` is cheap and deterministic. Most of the cost comes
from `task`, so this split is the running cost.
</details>

**2. Why put "if there is no material, do nothing" in the script stage?**

<details>
<summary>Answer</summary>

**Because if collection failed and the script stage runs anyway, it invents a
plausible script out of nothing.** In a pipeline running while nobody watches, a
wrong result hardens in place.
</details>

**3. Why does only publishing need a person?**

<details>
<summary>Answer</summary>

**Because it is irreversible.** Collection, script, audio, images and render can
all be remade; publishing is hard to retract. The guardrail criterion applies
directly.
</details>

---

**That completes the Extending it part.** From here you can take HyperTeams
beyond the screen — **joined to other programs and running by itself.**

If something is stuck → [Troubleshooting](/guide/ht-troubleshooting) ·
[Guide contents](/guide)
