# The away run, 19 to 20 September 2026
<!-- Role: the plan for the twenty-four hours rian is away from 19 Sep ~10:30 PDT. One
     orchestrating session runs the streams below in order, as workflows of subagents, in the
     worktree on branch `claude/collectors-live-page-plan-8951e2`. Rian answers questions in
     that session from his phone; every other rule is OVERNIGHT-RULES.md.
     RUN CLOSED 20 Sep 01:41 PDT (section 8's last row). This file is now RETIRABLE: delete it the
     day rian clears `do-review-the-away-run-of-19-to-20-sep-the-plan-s-state-tab` on the running list. Until then it is
     the only timeline of the run, and the handoff's 2026-09-20 entry links to it. -->

Written by the orchestrating session from rian's ask of 19 Sep 10:00: nine things to build for
the soft launch (Monday 21 Sep), one trigger, no terminal while he is away, Telegram at
milestones, questions to him in the session with a two-hour judgement-call deadline.

**Nothing here is rian's decision.** Every call is a working assumption a stream proceeds under,
recorded so he can overturn it in one sentence. What it costs to overturn is stated.

---

## 1. How this run works (read this before anything)

- **One orchestrator.** The session that wrote this file runs each stream as a workflow: a
  design or scout phase where the brief asks for one, then one subagent per task in order,
  then an adversarial review of the stream's diff, a fixer, and a structured hand-back. The
  orchestrator deploys, launches collection windows, relays questions, and sends Telegram.
  Streams never overlap in the tree. If the orchestrating session dies, any owner session can
  resume from section 5: every stream is also a `/stream-aw<n>` command in `.claude/commands/`.
- **Where.** All work in the worktree
  `/srv/apps/dutyfreeprofessor/.claude/worktrees/collectors-live-page-plan-8951e2` on branch
  `claude/collectors-live-page-plan-8951e2` (master `05941b4` is its base; staging runs it as
  0.52.0). `.venv-dev` and `main/web/node_modules` are symlinked there.
- **Deploys are authorised for staging only, by the orchestrator, at the deploy points in
  section 3, and only when the gate in section 4 is open.** A deploy is: `main/check.sh` green
  in the worktree; in the main checkout `git merge --ff-only claude/collectors-live-page-plan-8951e2`;
  `pg_dump` to `backups/` if a migration rides; `srv-gw deploy --project dutyfreeprofessor --build`;
  the after-deploy backfills the stream named; `srv-gw security-audit`; `curl /api/health`.
  **Never production.** `deploy/production.sh` is not run in this period; the launch plan (AW6)
  is a document for rian to execute.
- **The main checkout is dirty on purpose** (the sweep's logs under `.logs/runs/sweep-2026-09-19b/`,
  `.logs/runs/uptime.log`, rian's move of `notes/image-ask-2026-09-14/`, and the untracked
  `.app.env.example`, which AW6 edits in place and never commits). No stream commits a change to
  any of those paths, or the fast-forward at the next deploy point fails.
  Adam's images are untracked and exist only in the main checkout:
  `/srv/apps/dutyfreeprofessor/notes/images for the site/` (read from that absolute path; never
  commit an image; the runtime home is `uploads/`, gitignored and mounted at `/srv/uploads`).
- **Network.** A collection runs only in an announced window (`.logs/runs/window-<date>-<name>.md`),
  launched by the orchestrator through the admit-queue and guard scripts, never by a build
  agent. A build agent may read one `robots.txt` per host to test code, and AW1's scout may
  fetch `robots.txt`, the discovery page and one listing page per candidate host under the
  crawl delay (and one read of a platform's storefront list), inside its own announced window.
  AW3 may download Adam's chosen Pernod picture URLs and probe Wikidata and Commons logos, one
  request per second, inside its own announced window (section 4). Every rule in `agents.md` "Legal and collection posture" binds: no login, any
  matching Disallow is a no, a block is a refusal, no stealth, content-validated fetches.
- **Telegram** (`srv-gw notify --from "Away"`): stream start, each deploy, each collection
  window opened and closed, each stream hand-back with the count, any question waiting for
  rian with its deadline, and the final summary. Never a secret, never a URL with a token.

## 2. Questions to rian: the protocol

A stream that hits a decision only rian can make does not stop. It records a `decide` item on
the running list with `--assumption`, proceeds under the assumption where it can, and returns
the question in its hand-back marked `critical` or `not critical`.

The orchestrator then:
1. posts the question in the session (rian reads it on the phone app), with the assumption and
   the deadline: **two hours from posting**;
2. sends one Telegram line: the question in a few words, and the time the judgement call is made;
3. keeps working on whatever does not depend on the answer; a `critical` question holds only
   the task that needs it, never the run;
4. at the deadline with no answer, proceeds under the stated assumption and says so in the
   session and in the handoff. An answer that arrives later is applied by a follow-up agent.

**Critical** means: proceeding under any assumption would be unsafe or would waste more than an
hour if wrong (a deploy that changes public behaviour, a new airport made public, anything
irreversible). Everything else is a judgement call the orchestrator makes and records.

## 3. The streams, in order

| # | Stream | Brief | Command | Needs deploy before it starts | Deploy after |
|---|---|---|---|---|---|
| 1 | AW1 three new collectors | `AW1-collectors.md` | `/stream-aw1` | no | **D1**, then window W1 (the three run) |
| 2 | AW3 pictures at three levels | `AW3-images.md` | `/stream-aw3` | no | with D2 (the image import runs after it) |
| 3 | AW2 featured selection v3 | `AW2-featured.md` | `/stream-aw2` | no (reads AW3's tiers from code) | **D2** |
| 4 | AW4 the live collectors page | `AW4-live-page.md` | `/stream-aw4` | no | **D3**, then window W2 (a short live test) |
| 5 | AW5 launch surface | `AW5-launch-surface.md` | `/stream-aw5` | no | **D4** |
| 6 | AW6 the launch plan | `AW6-launch-plan.md` | `/stream-aw6` | no | with D4 if its one code change lands in time |
| 7 | AW7 the quality pass | `AW7-quality-pass.md` | `/stream-aw7` | D4 | if it fixes anything |

AW3 runs before AW2 on purpose: the featured algorithm's picture tiers read provenance fields
that AW3 defines and fills. AW1 is first because its collectors need a deploy and then hours of
crawl time, which overlaps with AW3 and AW2's code work. AW7 runs only if time remains.

Rian's list, mapped: 1 and 2 → AW1 and window W1 · 3 → AW4 · 4 → AW3 · 5 → AW2 · 6 → AW6 ·
7, 8, 9 → AW5 · "double check against the proposal" → AW6.3, with the first answer in section 6.

## 4. The deploy gate, and the collection windows

**A deploy kills every collector in the app container.** The gate is open only when both are
true: `docker exec dutyfreeprofessor-app python -m app.cli status` lists no source `running`,
and no `app.cli collect` process exists in the container (the check `finish.sh` uses). The
sweep running at the time of writing (`window-2026-09-19b-sweep.md`: `avolta-ath` and
`extime-paris` still going) closes itself: `finish.sh` appends the outcome to the window entry
and tells rian on Telegram. The orchestrator polls the gate every twenty minutes while waiting.

Windows this run opens, each announced in `.logs/runs/`, each launched with a copy of the
admit-queue and guard scripts from `sweep-2026-09-19b/` (a 3.4 GB shed ceiling, the memory trace):
- **W1, after D1:** the three new sources, in parallel (each waits its own host's crawl delay).
  Expected two to three hours if any host publishes `Crawl-delay: 60`.
- **W2, after D3:** one fast source with `--limit 30`, to exercise start, pause, resume and stop
  on the new page. Minutes.
- **The images window, during AW3** (`window-2026-09-19-images.md`, written by AW3, not a
  collection): the Pernod CDN downloads `images stage` makes for chosen entries (up to about 437)
  and the `images logos --limit 3 --check` probe of Wikidata and Commons, one request per second,
  the bot user agent, counts expected in the entry. A full `images logos` over every brand is
  rian's to run, not the orchestrator's.

While a window is open no deploy happens; code work continues.

## 5. Resuming without the orchestrator

From an owner terminal, in the worktree above: read this file, `git log --oneline -15` to see
which streams' commits (`AW1:` … `AW7:`) have landed, `.logs/handoff.md`'s top entries for the
last hand-back, then start the next stream's command and paste its goal line. The deploy points
and the gate are the same whoever runs them.

## 6. The proposal, double-checked against the plan (the quick answer rian asked for)

Every accepted quote line (`/quote`) against `import/progress.json` on 19 Sep 10:00, **corrected
on 20 Sep against the tree and staging** by AW6.3. The long form, with a proof per line, is
`.logs/planning/launch-plan-2026-09-19.md`, last section; where the two disagree that one is right,
because it checked things rather than statuses.

- **Done:** production environment (E1 to E7), the sixteen then nineteen airports (A10, A6,
  A13; nineteen `visible` on staging, of which SIN at 27 listings and ICN at 199 are thin),
  crawlable HTML (B1, B2), structure proposal (B3, P2), airport pages (B4; editorial on
  Heathrow only), brand pages (A8, A9, B5), mega menu (C2), featured prominence (C3, and AW2's
  v3 behind it), articles table and import (D1), email capture and export (D3), awards strategy
  document (F1), the merge session (M1 to M7).
- **Waiting on Adam, not on us:** D2 (his articles; none are in the database, and AW5 puts ten
  samples of ours in front of him), F2 (the picker landed with 78 tests **and is wired into
  `catalog_queries`**; what waits on Adam is his answer to the strategy card, and on us the pin
  through a recorded decision).
- **Not done, ours:** the **`core-quality`** line (Q1 to Q4 are done, but **I1** and **P6** are
  the rest of it; AW7 covers I1); **`categories`** (A7 done, **A11** blocked by the Paco Rabanne
  and Rabanne split, not by effort); **`core-brand`** and **`sponsor-slots`**, which both hang on
  **C5** (D4's slots are in seven templates but the registry is empty and the placements were
  rejected on 11 Sep).
- **The three-collector line, corrected:** Paris and Seoul are delivered (`extime-paris` 7,048
  prices; `shilla-icn` 192, ICN visible). Singapore is thin, not blocked: **R4's 403 was answered
  on 13 Sep** (`.logs/runs/window-2026-09-13-changi-retry.md`, twenty pages at 30 s, nineteen
  prices, no refusal), and 30 s is now the source's pace. Finishing it is one five-hour run over
  the 622 addresses in `import/changi-targets.json`.
- A11 and C5 need rian's word on whether they are still launch scope; see the questions in the
  session. Everything else above is either delivered or Adam's.

## 7. Working assumptions taken by the orchestrator (rian's to overturn)

**Rian, 19 Sep 10:25, in the session: "Yes to all four, go ahead."** The first four below are
therefore his decisions: staging deploys at the deploy points with master fast-forwarded; new
airports hidden until he looks; A11 and C5 are not in this run and are flagged in the launch
plan; sample articles are flagged and never reach production. The rest stay assumptions.

- **Staging deploys are authorised** at the deploy points, gated as above, with master
  fast-forwarded to the away branch each time. *To overturn: say so; the streams then hand
  back "green and ready" and nothing runs until you deploy.*
- **New airports from AW1 stay hidden** (`app.cli shops` visibility off) until rian looks at
  them; their collectors run regardless. *To overturn: one `shops` command per airport.*
- **Sample articles are flagged as samples** and never reach production; the launch plan
  excludes them. *To overturn: publish them for real, one command.*
- **The featured methodology page is public and `noindex`** until rian approves its address.
- **The page-status badge shows to the owner and the admin level only,** never in the
  server-rendered body.
- **Adam's images outrank every fetched one**, and a brand logo never appears as a product's
  image in structured data.
- **No production deploy, no production migration, nothing on the droplet** for the whole
  period. Singapore is not retried. No host that has refused us is contacted again.

## 8. Orchestrator state (updated by the orchestrating session as it goes)

The stream runner is one Workflow script, reused per stream with `args` (`stream`, `title`,
`brief`, `command`, `tasks`, `design`, `base`, `started`):
`/home/rian/.claude/projects/-srv-apps-dutyfreeprofessor--claude-worktrees-collectors-live-page-plan-8951e2/ff9443dc-b6ba-4c4c-b319-45375261734c/workflows/scripts/away-stream-wf_c0e259a5-84c.js`
(phases: Ground, Design when `design: true`, Build one agent per task, Review with three lenses
and two refuters per finding, Fix, Handoff). A 25-minute session cron is the fallback wake-up.

| When (PDT) | Event |
|---|---|
| 19 Sep 10:58 | Plan and briefs committed `ca3664d`; AW1 launched (run `wf_c0e259a5-84c`, base `ca3664d`) |
| 19 Sep 12:05 | AW1 handed back `ec1ffd2`: avolta-las, avolta-lgw, avolta-hel (hidden), W1 scripts under `.logs/runs/new-collectors-2026-09-19d/`; no review finding survived. ATH finished; Extime still running, D1 waits. AW3 launched (base `ec1ffd2`) |
| 19 Sep 13:22 | Sweep closed 13:12 (11 ok, 10,266 prices, peak 1,590 MiB). **D1 deployed**: master fast-forwarded to `6804f65`, dump `backups/dfp-2026-09-19-pre-D1.dump`, check green in the main checkout, health ok. **W1 opened 13:24**: avolta-las, avolta-lgw, avolta-hel via `.logs/runs/new-collectors-2026-09-19d/` (admit, guard, finish running). No deploy until it closes. AW3 still running |
| 19 Sep 13:58 | AW3 handed back `832f57c` (2,230 tests): migration `aw3b1c2d3e4f`, manifest with 82 pictures staged in `uploads/images/`, cascade, `/images` by level; review fix: Wikidata/Commons refused under robots (`decide-wikimedia-api-under-disallow` for rian). W1: LGW ok 473, LAS and HEL still running. AW2 launched with the design panel (base `832f57c`) |
| 19 Sep 16:45 | W1 closed (LAS 265, LGW 473, HEL 442 prices, all ok, all hidden; window file folded into the branch). AW2 handed back `42021d2` (2,331 tests): featured v3 behind every featured sort, the pin as a ledger decision, `/how-we-choose`, migration `aw2c1d2e3f4a`; two review findings fixed. **D2 starting** (migrations `aw3b1c2d3e4f` + `aw2c1d2e3f4a`, then places, image_sources, images import, images lines). AW4 launched with the design panel |
| 19 Sep 16:50 | **D2 deployed** (master `cf0d853`, dump `backups/dfp-2026-09-19-pre-D2.dump`, head `aw2c1d2e3f4a`): places 3 created; image_sources 1,392 moved; images import brand 25, line 36, variant 21; images lines 545; featured evidence 9/9 home, perfume, whisky, 8/9 makeup; LAS, LGW, HEL hidden. `/how-we-choose` answers 302 to an anonymous request as the members-only site should. Issue filed: `.webp` served as octet-stream (AW7). AW4 running |
| 19 Sep 19:35 | AW4 handed back `767462a` (2,401 tests): migration `aw4a1b2c3d4e`, the control hook, six routes under sources.manage, LiveOut with nine states, the LiveBoard page, the W2 plan; four review findings fixed. Reported breach: one local-API Start in AW4.4 spawned a real avolta-hel collector for ~12 s (2 requests, 0 prices). **D3 starting** (freeze marker, dump, build); AW5 launched (base after this commit). W2's page half asked of rian, deadline 21:40 |
| 19 Sep 19:50 | **D3 deployed** (master `c492374`, dump `pre-D3`, head `aw4a1b2c3d4e`, freeze marker written and removed, audit clean; reviewed dev copies dropped). Rian at 19:45: Fable credits low, use Opus. AW5 stopped (only its registrations had landed) and relaunched with `model: opus`; every later stream runs on Opus. The runner script now takes `model` in args |
| 19 Sep 20:02 | **W2 done** (shell lane 12/12; page half 11/12 through rian's browser: runs 186, 187, 188). **Bug found:** a vanished product's 404 in a recheck marked shopify-yul refused; fix in progress on Opus (404/410 = not found, `backfill false_refusals`), rides D4, then Montreal's recheck steps repeat. Sources left: yul 3 s discover, sal 3 s, lgw 30 s. Closing sections in the main checkout's W2 file |
| 19 Sep 22:12 | AW5 handed back `9561301` (2,496 tests, Opus): page status (four words, badge for owner and admin), ten sample articles flagged and their import lines, `/articles` server-rendered and noindex, email capture proven, migration `aw5d1e2f3a4b`; two review findings fixed. The 404 fix `6ce158b` is on the branch. **D4 starting**; AW6 launched on Opus (base `9561301`) |
| 19 Sep 22:15 | **D4 deployed** (master `6a1a989`, dump `pre-D4`, head `aw5d1e2f3a4b`): level_permissions granted admin +pages.status; false_refusals moved run 186 to error (shopify-yul idle); ten sample articles imported and published; badge absent from the served body; audit clean. Next: Montreal recheck on the live page, AW6 running (Opus) |
| 19 Sep 22:25 | **W2 complete**: Montreal recheck on the fixed build, run 189 ok (30 requests, 25 checked, 5 missing, 25 prices). The closed W2 window file folded into the branch (the main checkout's copy is `git checkout --`'d before the next fast-forward). Issue noted: the collapsed row's summary line shows the previous run's prices/checked while a run is live. AW6 running |
| 19 Sep 23:38 | AW6 handed back `e722648` (2,509 tests, Opus): `.logs/planning/launch-plan-2026-09-19.md` with the proposal double-check, `SITE_ROLE` (development surfaces 404 on live; `role` on /api/health), the `LAUNCHED` marker in `deploy/production.sh`; one review finding fixed. Six decides for rian, the legal pages one blocking (assumption: launch Monday, pages follow). **D5 starting**; AW7 launched on Opus (base `e722648`) |
| 19 Sep 23:43 | **D5 deployed** on the second attempt (the first fast-forward was refused by the main checkout's dirty copy of the W2 window file and a piped exit code hid it; the copy was restored to its identical committed version). Master `f8f0af3`, dump `pre-D5`, health `role: staging`, no launch marker present, audit clean. AW7 running (Opus) |
| 20 Sep 01:40 | AW7 handed back `2c00a44` (2,535 tests, Opus): route sweep script (243 keys, 0 leaks), nine pages at 1440 and 390 with no breaks, four accessibility fixes, timings, twelve issues filed, I1 done on /plan; no review finding survived. **D6 starting** (the accessibility fixes; frontend only). Close-out delegated: handoff entry, /checkpoint, final Telegram |
| 20 Sep 01:41 | **Run closed: D6 deployed** (master fast-forwarded `f8f0af3..dec7aa8`, the AW7 accessibility fixes, frontend only; `check.sh` green, health `{"status":"ok","version":"0.52.0","database":"ok","role":"staging"}`, collect marker removed, audit 0 new findings). Handoff and checkpoint written (NOW block, sweep 167 open with nothing this session closed, docs-check 0 fail / 9 warn, Claude inbox empty since 19 Sep); the review item `do-review-the-away-run-of-19-to-20-sep-the-plan-s-state-tab` added for rian; final Telegram sent. Nothing left running |
