---
description: Stream B: server-rendered product body, SEO corrections, structure page sweep, then hub pages
---

You are running **Stream B** of the Duty Free Professor soft-launch build, as an
autonomous session. Start by running `pwd`; it must be `/srv/apps/dutyfreeprofessor`, or stop.

Read first, in this order: `.logs/planning/streams/OVERNIGHT-RULES.md` (binding), then your
brief `.logs/planning/streams/B-pages.md`, then `agents.md`, `brief.md`, build plan `.logs/planning/build-plan-2026-09.md`
sections 2, 7 and 9, and `.logs/decisions-for-rian.md` (generated from the running list: `python3 main/scripts/items.py list --decisions` shows what rian has decided).

Then follow the brief's ordered task list exactly as the rules describe: one task at a time,
verified and committed, recorded on `/plan` with `main/scripts/plan-set.py`. Delegate mechanical
subtasks to cheaper models per the rules and verify their output yourself. Never deploy; never
run a network collection your brief does not name for tonight; when blocked on a rian-only
decision, record it and continue. When the brief is exhausted or eight hours have passed, write
the final handoff entry, run `/checkpoint`, and stop the loop.

The body you render must match what the SPA shows; anything else is cloaking. Keep the PageSpeed work intact and re-measure after B1.

## Goal, and exactly when you are done

**Goal:** Stream B Wave 2: airport pages, then the crawl surface (IndexNow, per-bot robots, RSS, llms.txt, Dataset page), then brand pages once the brands table exists, to the verified-and-committed standard.

**You are done when ALL of the following are true**, and not before:
1. Every one of these task ids in `import/progress.json` is `done`, or is `blocked` with a
   note saying why: **B4**, **B7**, **B5**. (Check with `python3 -c "import json;d=json.load(open('import/progress.json'));print({t['id']:t['status'] for s in d['streams'] for t in s['tasks'] if t['id'] in ['B4', 'B7', 'B5']})"`.)
2. `../.venv-dev/bin/pytest tests -q` passes and `main/check.sh` prints "checks passed" on the
   current tree.
3. Every finished task has its own commit prefixed `B:` (`git log --oneline | grep "^[0-9a-f]* B:"`).
4. A handoff entry for this session exists at the top of `.logs/handoff.md` (≤25 lines: what
   landed and how it was verified, what is blocked and why, what is next, what you need from
   rian), and `/checkpoint` has been run.
5. Anything only rian can decide is on the running list (`python3 main/scripts/items.py add
   --kind decide ...`) with the assumption you proceeded under; issues you found or fixed are
   added or resolved there too.

A task you cannot finish is not a reason to keep going or to stop early: mark it `blocked`
with the reason, record the decision if one is needed, and move to the next. If all tasks
are done or blocked, finish steps 2–5 and stop. "If time remains" items are optional and only after the list above is complete; B6 waits for rian's §10 #10 answer.

## Gates for this run (binding)
- **B4 first.** Airport pages on the agreed structure: ItemList + CollectionPage + Airport markup, sitemap entries, breadcrumbs, internal links, server-rendered body like the product page. Airport slugs from `locations`, never from raw strings.
- **B7 next**: it depends on nothing another stream owns. IndexNow ping reads the collector's changed-URL list; per-bot robots from `robots.py`'s `BOT_NAME` policy in reverse (what we allow, not what we send).
- **B5 (brand pages) only after Stream A has committed migration #3 and the brand fold (A8, A9 `done` on `/plan`)**; brand slugs come from the brands table. Until then, mark B5 `blocked` with "waiting on A8/A9" and stop after B7.
- **B6 (category pages) is not in this run**: §10 #10 is rian's and undecided.

## The goal line (rian pastes this after the first turn ends)
Start the session with `/stream-b`. When that first turn ends, paste this one line; the evaluator
then keeps the session working, turn after turn, until the condition is met or judged impossible:

```
/goal Stream B of the Duty Free Professor build is complete: in import/progress.json every one of the task ids B4, B7, B5 has status done, or blocked with a note saying why; main/check.sh has printed "checks passed" on the final tree; every finished task has its own commit prefixed "B:"; a handoff entry for this session is at the top of .logs/handoff.md and /checkpoint has been run; anything only rian can decide has been added to the running list with main/scripts/items.py. Or stop after 80 turns.
```

