# scout — handoff

> Append-only, **newest first**. What the last session did, what the next should
> pick up, what is blocked. Read this second (after `brief.md`) when orienting.
> Clear-after-read: once an entry is acted on it can be trimmed.

## 2026-08-18 (later) — M4: the full BW Auth suite (v0.4.1)

**Everything in `.logs/planning/04-full-auth-suite.md` shipped and is live.**
View As (readonly + owner-only act mode, un-hideable banner, central oversight),
the level editor (levels as data, described permission catalog, custom
permission strings honestly badged), per-project + app-wide assignment with
per-project assignability, invites (gateway-emailed), resets (self/admin), the
anonymous forgot-password stopgap, the access matrix, and the Auth status/sync
tab. People page is now tabbed: People | Levels | Access | Auth.

**Two deliverables for the BW Auth session now live in `notes/`:**
`bw-auth-feedback.md` (the findings) and **`bw-auth-one-prompt-request.md`**
(the requirements prompt for the one-prompt app standard: SQLAlchemy store,
mountable admin API, vendored React admin pack with typeahead UserPicker +
filterable AdminTable + the account-menu IA, directory-search and in-app
password-change gateway endpoints, the scaffolder, and the conformance test
pack). Three decisions in its §7 need rian before the auth side builds.

**The findings file: `notes/bw-auth-feedback.md`** — works /
tensions (T1–T11) / gaps (G1–G5) / bugs, each with the Scout artifact to look
at. Hand that file to the BW Auth session.

**Verified:** 92/92 pytest (real Postgres, all BW calls stubbed+recorded);
30/30 live checks plus view-as edge cases (admin target: capabilities visible,
writes still blocked; admins hold no view-as by default). Suite caught two real
0.4.0 bugs before rian ever touched it — per-project assignability and the
name-keyed view-as rank — fixed in 0.4.1 (see CHANGELOG).

**Rian's manual pass (the things automation deliberately did NOT fire):**
1. View As from the UI: "View as…" in the top bar → darren → walk his view →
   banner → Return to yourself. Check the amber row on the access console's
   Live page while active.
2. Create your two example levels in People → Levels (e.g. `manager` and a
   `coordinator` with `scout.project.members` + assignable: reviewer), grant
   the coordinator level to someone on ONE project, View As them, and confirm
   the world looks right.
3. Invite a real user (People → Invite person — use an inbox you control) and
   walk the email → set password → sign in → land on Scout loop.
4. "Send reset link" for a user who has NEVER signed into Scout — the outcome
   answers feedback item T6 (two docs disagree on the scoping rule).
5. Check auth.bowden.works/account (via the Account link in the top bar) offers
   password CHANGE, not just Google linking — feedback item, unconfirmed.

**Known gaps (recorded in brief.md):** owner read-only-viewing an ADMIN target
sees clickable admin controls on Options/Materials that rely on the middleware
403 rather than being disabled; invite username min-length differs from add
(2 vs 1); the forgot-password stopgap belongs centrally (G2).

## 2026-08-18 — M2: per-project access levels (v0.3.0)

Scout's app-wide two-value role is gone. A person now holds a **named level**, and
that level is assigned **per project**, on the BW Auth accounts kit that the
id-auth session dropped into the tree (`notes/bw-auth-accounts-handoff.md`).
Plan and locked decisions: `.logs/planning/03-per-project-levels.md`.

**The model:** owner (`rian`, immutable, synthesized — not a row) → `admin`
(every project) → `lead` (one project: reviews AND sees its results) → `reviewer`
(one project, reviews only). Levels are rows in `app_levels`, so they are
editable without a code change.

**Live example of the thing that was impossible before:** `mchen` is a
**reviewer on League Law and a lead on Harbour Dental**. `darren` is now the
**lead** on League Law, so he will see his own project's rollup.

**Two real bugs found and fixed while building, both worth knowing:**

1. **Migration 0003 silently demoted admins.** It gave every existing membership
   the default `reviewer` grant, and a grant beats `all_instances` — so `adi`,
   an app-wide admin who happened to be a *member* of Harbour Dental, lost the
   ability to manage that one project while looking fine everywhere else. Caught
   by checking the data after migrating rather than trusting it. Migration 0004
   repairs the rows; `add_member` now clamps so it cannot recur; a regression
   test pins it. Deliberately scoping someone down on one project still works —
   it just has to go through the explicit `set_member_level` path.
2. **`client_website` was dropped on project create** (v0.2.1, earlier the same
   day). The schema accepted it and the dialog sent it, but the router never
   passed it through. The overnight suite missed it because it tested create and
   update separately and never asserted the created value round-tripped.

**Verified:** 61/61 pytest against real Postgres; 14/14 live HTTP checks against
the deployed app and real data (levels, 404-vs-403 asymmetry, owner short-circuit,
deactivation, project visibility counts). Details in `.logs/diary.md`.

**Answer relayed to the auth side:** central stays **read-only** for Scout access
(D24). Scout is the sole authority for its own assignments; no write-back API
wanted. The open question in `notes/bw-auth-accounts-handoff.md` §9 is closed.

**Still open:**
- No browser walkthrough of the new People / per-project level screens — API and
  `tsc` only, same gap M1 left.
- A backfill of central visibility runs at startup (`sync_all`); confirm the
  access console shows the per-project levels as expected.
- Permission resolution costs a few queries per project in the list view. Fine at
  three projects, worth a per-request cache before dozens.

## 2026-08-12 (overnight) — M1 shipped: v0.2.0 deployed, League Law seeded

**Every stage of `.logs/planning/02-m1-direction-loop.md` shipped.** A–G, nothing
cut. v0.2.0 is live and healthy; 42/42 pytest green against real Postgres; the
36-check HTTP verification of the whole loop passed; League Law is seeded with
5 draft options. Details in `.logs/diary.md`.

**The League Law project:** https://scout.bowden.works/projects/3
It is the project you created in the browser last evening ("League Laq" — I kept
it and renamed it rather than duplicating): now **"League Law — 2026 redesign"**,
slug `league-law-redesign`, active, client_website set, **darren is a member**
(account re-provisioned via a live BW lookup). Materials attached: the distilled
direction in Darren's own words, the full email thread, current-site captures
(desktop + mobile), and the site link. Five research options imported through
the real import endpoint, **all DRAFT — nothing published, nobody notified**:

- **Option A — Align Family Law** (Victoria BC) · warm & team-first
- **Option B — Reaves & Lee** (Dallas) · approachable & reassuring
- **Option C — ELCMD** (Maryland) · crisp & direct
- **Option D — Burnham Law** (Colorado) · bold & confident — the distinctiveness ceiling-test
- **Option E — Podhurst Orseck** (Miami) · polished & authoritative

Each has a `why_selected` tied to Darren's quoted criteria and `design_notes`
for the mockup phase. Deliberately excluded: Avize (ex-Acheson — a direct
Victoria PI competitor, awkward even anonymized), Bick Law (surreal full-bleed
hero, exactly Darren's stated dislike), Roulston (video hero), HagEstad
(mountain-scenery hero), YLaw/ALH (bot walls — wouldn't capture cleanly).

**Rian's 10-minute morning checklist:**
1. Open https://scout.bowden.works/projects/3 → **Options** tab. Read each
   option's descriptor + why_selected; click the thumbnails to see the full
   captures; tweak any wording (Edit) or order (Move up/down).
2. Optional: **Preview the review** (button, top right) to walk the exact flow
   Darren will see — your ratings save into your own row and stay out of the
   client rollup.
3. Publish the options you're happy with (Publish button per option).
4. Tell Darren it's ready. His email drafts itself — *"You asked for examples
   you could rank — here they are: https://scout.bowden.works — sign in and it
   walks you through it, ~10 minutes."* (His BW password is set; he signs in at
   the site and lands directly in the review.)
5. Note: Option E (Podhurst) has a small capture artifact — the sticky nav
   painted mid-page in the full-page shot. Recapture with
   `tools/capture/run.sh` and re-import if it bothers you; re-import updates in
   place and keeps everything else.

**When reviews come back (Stage 2 of the pipeline):** project → **Results** tab
shows the rollup (distributions, aspect tallies, notes, final picks, biggest
split). **Download Scouting Report** produces the markdown artifact — fill in
its "Direction (designer's synthesis)" section and feed it to the mockup
session; its footer tells that session how to use it.

**Open items for a next session:**
- First human walkthrough of the review game on a real phone (no browser-level
  test coverage exists — API + tsc only).
- If the other stakeholders (Janice, Rob Graham, Natasha) should review too:
  `srv-gw id-user-create` + `id-user-set-password` for each, then add them on
  the project's Overview tab. D7 means their reviews stay independent.
- The pytest suite runs via `docker cp`/`docker exec` (invocation in diary);
  wiring it into a pre-deploy step is a nice-to-have.

## 2026-08-11 (late) — M1 planned; overnight build queued

Darren's BW password is set — the M0 blocker below is cleared. The real League Law
email thread landed at the workspace root (`League Law's next phase - time for a new
website.eml`); Darren's final message explicitly asks for ranked examples, which is
the product.

**The M1 plan/prompt is `.logs/planning/02-m1-direction-loop.md`** — it is written
to be executed verbatim by an autonomous overnight session (Fable orchestrating,
sonnet/haiku subagents). If you are that session: read it and go. It supersedes the
M1 sketch at the bottom of `01-baseline.md`.

## 2026-08-11 — M0 baseline landed (v0.1.0)

**Landed.** v8 workspace scaffolded; the app rebuilt from the nginx placeholder onto
React + FastAPI + Postgres. BW Auth (Pattern B) sign-in, default-deny API, two roles,
scouting projects with membership as the access boundary. Deployed and exercised against
the live host — see `.logs/planning/01-baseline.md` for what was verified.

**Blocked on rian — one command.** Darren's BW account exists but has no password, so he
cannot sign in yet. It must be set interactively by the owner; it must never be typed
into a chat:

```bash
srv-gw id-user-set-password --user darren
```

`rian` and `adi` already had BW accounts and were seeded as Scout admins on first boot.

**Already proven, so don't re-derive it.** The full flow was exercised against the live
app and the live BW server: create project → resolve `darren` through BW → provision →
add to project → admin sees all, user sees only theirs → re-login preserves the role an
admin set. All sixteen assertions passed, and the script cleaned up after itself, so the
database is empty. What that run could *not* cover is the browser half: the OAuth
round-trip and the session cookie.

**Next session picks up:**

1. **Walk the browser half.** Rian signs in at `https://scout.bowden.works`, creates a
   project, opens it, "Add person" → `darren` with role User. Then Darren signs in and
   confirms he sees that project and nothing else. This is the only part of M0 a human
   has to do, and it is the part that proves the OAuth round-trip works in a real browser.
2. **Write the authorization tests.** pytest + httpx over the HTTP boundary: a user must
   get 404 (not 403) for a project they are not on, and must not reach any admin route.
   The service layer is proven; the router layer is not.
3. **Then talk M1 before writing any of it.** The four open product questions are at the
   bottom of `.logs/planning/01-baseline.md` — what gets crawled, what the client reacts
   to, what the game is, and what the output artifact looks like. The last one should be
   designed first.

**Worth knowing:**

- Deploys must be `srv-gw deploy --project scout --build`. Without `--build` the SPA
  serves stale code.
- The TypeScript API client is generated during the image build and is not committed. A
  Pydantic change that breaks the frontend fails the build rather than shipping.
- `HEAD /` returns 405 (GET only). Harmless in a browser; `/healthz` is the health check.
