---
description: Stream G: the complete airport page (opening hours where robots allow, hours by hand with provenance, featured by family, shopping-feature filters, a comparison preset, category-at-airport pages)
---

You are running **Stream G** of the Duty Free Professor build, as an autonomous session while
rian is away. Start by running `pwd`; it must be `/srv/apps/dutyfreeprofessor`, or stop.

Read first, in this order: `.logs/planning/streams/OVERNIGHT-RULES.md` (binding),
`.logs/planning/streams/AWAY-PLAN.md` sections 2 and 3 (the standing rules for the away period
and the assumptions you proceed under), then your brief `.logs/planning/streams/G-airport.md`
(it names what already exists; build on it), `agents.md`, `brief.md`, build plan
`.logs/planning/build-plan-2026-09.md` sections 2 and 9, `main/docs/COLLECTORS.md`,
`main/docs/ARCHITECTURE.md`, `python3 main/scripts/items.py list --decisions`, the top three
entries of `.logs/handoff.md`, and `main/web/src/lib/airportTemplate.ts` in full (the gaps
panel is your task list, in the shopper's words).

Then work the brief's tasks in order, G1 to G7, one at a time, each verified and committed and
recorded on `/plan` with `main/scripts/plan-set.py` (`doing` when you start, `done` with a
`--note` that says what proves it, `blocked` with why). Delegate mechanical edits (fixture
trimming, a parser's test scaffold, a CSS pass) to a cheaper model and verify the diff yourself;
never delegate the robots verdicts, the provenance wording, or anything a shopper reads.
**Stream R2 and Stream M are in the same tree at the same time:** their columns are named under
Must not touch in your brief; a need in their files is an issue on the running list for them
(`items.py add --kind issue --owner M|R --by "Stream G"`), never an edit. Every migration is
rehearsed on `dfp-devdb` (127.0.0.1:5433) in a fresh copy of the newest nightly dump, with
`alembic heads` checked immediately before writing and before committing.

The network budget of this session is one robots.txt read per airport operator domain and the
hours pages those robots allow, each run announced in `.logs/runs/`; nothing else is fetched,
and no retail collector runs. Never deploy, never touch production, never move an address.

The one outcome that matters: when you are done, an airport page on staging shows its opening
hours with an honest provenance line, featured products by family, the shopping-feature
filters, a link that opens the comparison tool on that airport, and its category pages where
the coverage bar is met; and the gaps panel names only what is Adam's to write.

## Goal, and exactly when you are done

**Goal:** opening hours collected where robots allow and entered by hand where not, with
provenance; featured by family; the shopping-feature filters; the comparison preset; the
category and category-at-airport pages, 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: **G1, G2, G3, G4, G5, G6, G7**. (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 ['G1','G2','G3','G4','G5','G6','G7']})"`.)
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 `G:` (`git log --oneline | grep "^[0-9a-f]* G:"`).
4. A handoff entry for this session exists at the top of `.logs/handoff.md` (≤25 lines: what
   landed and how it was verified, the robots verdict per operator domain, the migration id and
   the after-deploy commands in order, what is blocked and why, what rian must do), and
   `/checkpoint` has been run.
5. Anything only rian can decide is on the running list (`python3 main/scripts/items.py add
   --kind decide --by "Stream G" --blocks <task> --weight ...`) with the assumption you proceeded
   under; the two issues for Stream M and Stream R3 named in G3 are filed.
6. Any rehearsal database, local server and `main/static` symlink are gone.

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 to 6 and stop. If eight hours have passed, mark every
unfinished task `blocked` with the note "eight-hour cap; resume with /stream-g", write the
handoff, run `/checkpoint`, and stop.

## The goal line (rian pastes this after the first turn ends)
Start the session with `/stream-g`. When that first turn ends, paste this one line:

```
/goal Stream G of the Duty Free Professor build is complete: in import/progress.json every one of the task ids G1, G2, G3, G4, G5, G6, G7 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 "G:"; a handoff entry for this session is at the top of .logs/handoff.md naming the robots verdicts, the migration id and the after-deploy commands, and /checkpoint has been run; anything only rian can decide has been added to the running list with main/scripts/items.py; no retail collector was run and nothing was deployed. Or stop after 120 turns.
```
