---
description: Stream R (R1): the accounts and login system, autonomously, to green and ready
---

You are running **Stream R, phase R1** (the login system) of the Duty Free Professor build as an
autonomous session on Fable: you design, decide and review; sonnet subagents do mechanical work
you specify with an acceptance test, and you verify every diff they hand back. 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 the
design `.logs/planning/accounts-2026-09.md` in full (it is the decision record; do not redesign
what it decides, and record a deviation as a `decide` if you must), then your brief
`.logs/planning/streams/R-accounts.md`, `agents.md`, `brief.md`, build plan
`.logs/planning/build-plan-2026-09.md` sections 2, 4 item 9, 9 and 10, `main/docs/ARCHITECTURE.md`,
`main/docs/CLIENT-SURFACES.md`, `.logs/decisions-for-rian.md` (generated; read, never edit), the
two newest entries of `.logs/handoff.md`, and `python3 main/scripts/items.py list`. Then the kit
you will vendor: `/srv/system/id-auth/app-auth/ACCOUNTS.md`, `PACKS.md`, `VIEW-AS.md`,
`react-admin/README.md`, the four kit modules, `app-template/main/tests/conftest.py`, and
caddie's wiring `/srv/apps/caddie/main/app/{accounts,identity,view_as_policy,middleware}.py` as
the precedent. Read the kit; never edit it.

Every rule of the overnight session binds: never deploy, never run a collection, never run a
migration against the live database, never `git push`, never touch `.env`, `.app.env` or
`.bw-auth.env`, never print or invent a secret, never ask rian to paste one. Stream E may be
working in the same tree on `deploy/` and the production overlay; you never touch those, and
E never touches yours. Commit prefix **`R:`**, one landed task per commit, a body that says
what it prevents or what it cost. `../.venv-dev/bin/pytest tests -q` and `main/check.sh` before
every commit. Every `items.py add` and `resolve` carries `--by "Stream R"`.

## Recording progress

The lane and its tasks already exist in `import/progress.json` (lane `R`; ids `L1`, `O2`, `L3`
to `L11`). `python3 main/scripts/plan-set.py <id> doing "Stream R"` when you start one, `done`
with a `--note` that says what proves it, `blocked` with the reason. Never edit that file by
hand. A CHANGELOG line under `## Unreleased` for anything rian, Adam, Mark or a shopper would
notice; never bump the version.

## Delegation

Hand a sonnet subagent anything with a precise spec and an acceptance test: the vendoring
copy and manifest, test scaffolding from a written case list, docstring passes, the SPA pages
from a written component spec, bulk edits from a precise instruction. Verify its output
yourself (run the tests, read the diff). Never delegate the migration, `access.py`, the session
store, the password and token code, the directory adapter, the View As policy, any allowlist
or permission decision, any client-facing wording, or any judgment about a document.

## The rehearsal database

`dfp-devdb` (`127.0.0.1:5433`) may or may not still exist (item `do-dfp-devdb` is rian's, now
asking him to keep it through this night). If it does: restore the newest
`backups/dfp-nightly-*.dump` into a fresh `dfp_r` (`docker exec dfp-devdb psql -U dfp -d postgres
-c "create database dfp_r"`, then `docker exec -i dfp-devdb pg_restore -U dfp -d dfp_r --no-owner
--no-privileges < backups/<file>`; drop and create are separate commands). Point `DATABASE_URL`
at it (the scratch container's password is in its environment; never print it). If the
container is gone, rehearse the migration up and down on a fresh Postgres you start yourself on
`127.0.0.1:5433` with the same image (`postgres:16-alpine`, a throwaway password in its own
environment, removed when you finish), mark the L10 curl list as run against the empty schema,
and say so in the handoff. Local server:
`npm run build` in `main/web/`, `ln -sfn web/dist main/static`, `../.venv-dev/bin/uvicorn
app.main:app --port 8765` from `main/` with `PUBLIC_BASE_URL`, `ACCOUNT_OWNER=rian`,
`SITE_ACCESS=members` set and no `APP_ENV` (so the server runs as production and issues the
`__Host-dfp_session; Secure` cookie, which loopback accepts); curl with the Origin header on
every POST; remove the symlink; kill by port, never with a `pkill -f`
pattern that matches your own shell. Drop `dfp_r` when you finish. Never point anything at
the live database.

## The tasks

Work the brief's R1 list in order: L1, O2, L3, L5, L4, L6, L7, L8, L9, L10, then L11 if time
remains. Each task's scope, tests and acceptance are in the brief and the plan sections it
names. A task that needs more than about three hours is split, recorded on `/plan` with a
note, and the part that lands tonight is kept.

The plan-gate before code: your first handoff entry (prepended before the first commit) is a
short written plan naming the modules you will create, the migration's revision id, and the
security checklist of plan §5 with your answers.

## What you never do in this session
- Deploy, or run `srv-gw deploy`; say "green and ready" in the handoff.
- Run `srv-gw app-client-register`, `srv-gw id-gate`, `srv-gw add-dns`, or the server's
  `new-bw-app.sh` scaffolder; edit anything under `/srv/system/` or `/srv/apps/caddie/`.
- Mint `INDEXNOW_KEY`; set `SITE_ACCESS=public`; add a `Domain` to any cookie.
- Write a session token, an invite link, a generated password or a hash into a log, a test
  fixture name, a commit body, the handoff or the transcript, or into a URL path or query (the
  access log prints every request line; tokens ride in the link's fragment and a JSON body).
- Edit a vendored file; the fix is an upstream proposal in
  `.logs/planning/kit-upstream-proposals.md`.
- Touch `docker-compose.yml`, `deploy/`, the collectors, the browser sidecar, or a running
  collection; run a network collection or crawl.
- Build R2 or R3 (threads, notifications, the collection page, the scheduler).
- Edit `.logs/issues.md`, `.logs/decisions-for-rian.md` (generated), the build plan, or another
  stream's brief.

## Goal, and exactly when you are done

**Goal:** the account system of plan §4 is landed to the verified-and-committed standard, or
each unfinished task is `blocked` with its reason; the migration is rehearsed both ways on
`dfp_r`; the curl list and the browser round trip of plan §7 have run against a local server;
every document says what the code is; rian has one handoff that tells him what to set, what
to run after the deploy, and that the tree is green and ready.

**You are done when ALL of the following are true**, and not before:
1. Every one of **L1, O2, L3, L4, L5, L6, L7, L8, L9, L10** in `import/progress.json` is `done`,
   or `blocked` with a note saying why. (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 ['L1','O2','L3','L4','L5','L6','L7','L8','L9','L10']})"`.)
2. `../.venv-dev/bin/pytest tests -q` passes and `main/check.sh` prints "checks passed" on the
   current tree, with the vendor check and the conformance pack inside them; `bash
   main/docs-check.sh --strict` prints `0 fail`; `wc -l agents.md` is at most 140.
3. Every landed change has its own commit prefixed `R:`.
4. `.logs/issues.md` and `.logs/decisions-for-rian.md` were regenerated by `items.py export`
   after your last `add` or `resolve`, and every deviation from the plan is a `decide` with the
   assumption you proceeded under.
5. A handoff entry for this session is at the top of `.logs/handoff.md` (25 lines at most):
   what landed and how it was verified, what is blocked and why, the two environment lines
   rian sets, the dump, the deploy, every after-deploy command in order (the backfills, the
   three account commands, the invite links, `awards --rebuild` if it is still pending), the
   curl list to run, the owner-only `id-gate` step, and "green and ready"; `/checkpoint` has run.
6. `dfp_r`, the local server and the `main/static` symlink are gone.

A task you cannot finish is not a reason to keep going or to stop early: mark it `blocked`,
record the decision if one is needed, and move on. If all ten are done or blocked, finish 2 to
6 and stop. If you have been going for eight hours, mark every unfinished task `blocked` with
"eight-hour cap; resume with /stream-r", write the handoff, run `/checkpoint`, and stop.

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

```
/goal Stream R phase R1 of the Duty Free Professor build is complete: in import/progress.json every one of the task ids L1, O2, L3, L4, L5, L6, L7, L8, L9, L10 has status done, or blocked with a note saying why; main/check.sh has printed "checks passed" and "bash main/docs-check.sh --strict" has printed "0 fail" on the final tree; every landed change has its own commit prefixed "R:"; .logs/issues.md and .logs/decisions-for-rian.md were regenerated by main/scripts/items.py export after the last add or resolve; a handoff entry for this session is at the top of .logs/handoff.md naming the environment lines rian must set, the dump, the deploy, every after-deploy command in order, the curl list, the id-gate step and "green and ready", and /checkpoint has been run; the dfp_r database, the local server and the main/static symlink are gone. Or stop after 200 turns.
```
