# scout — brief

**What:** A tool for getting a client to clarity on website design direction. Scout
crawls competitor and reference sites, then presents the client with options they react
to in a light, gamified way. Their reactions become a defensible direction rather than a
committee opinion.

**Why:** "What do you want the site to look like?" is a question clients cannot answer in
the abstract, and the usual substitute — a mood board argued over in a meeting — produces
a direction nobody actually committed to. Reacting to concrete options is something people
can do well; Scout collects those reactions and turns them into a brief.

**Who:** Bowden Works staff run scouting projects (admins). Clients are invited into a
single project and only ever see that project (users).

**Status:** The direction loop (M1), per-project access levels (M2), and the full BW
Auth feature suite (M4: View As, level editor, invites, resets, access matrix) are built
and deployed. Findings from dogfooding the auth standard live in
`notes/bw-auth-feedback.md`. The loop: materials intake,
briefing export, research-package import, the per-person review game, results rollup,
and the Scouting Report. Scout makes zero AI calls — the research step runs in a Claude
session outside the app, connected through two file contracts (the briefing export and
the research import). Screenshot capture is host-side tooling (`tools/capture/`), not an
app feature.

## Where everything is
| Role | Lives in |
|---|---|
| Build plan, milestones, gates | `.logs/planning/` |
| Session handoff (what next) | `.logs/handoff.md` |
| Project constitution | `agents.md` |
| Human reference material | `notes/` |

## Stack

**React SPA + FastAPI + Postgres** — the server's standard app stack
(`/srv/projects/standards/react.md`). The running container is one Python process:
FastAPI serves both the API and the pre-built SPA. Node exists only in the image build,
which is why deploys are `srv-gw deploy --project scout --build` and take a couple of
minutes rather than being an instant restart.

The choice is driven by what is coming: crawling and screenshotting reference sites,
storing and comparing options, and eventually scoring feedback are all Python work, while
the feedback game needs genuine app-like UI.

**Authentication is BW Auth (Pattern B)** — Scout stores no passwords and has no login
form of its own. People sign in with the BW account the server owner created for them, at
`auth.bowden.works`. Scout keeps its own session and its own `app_accounts` table holding
the things BW does not know: access levels, and who is on which project.

## Access levels

Levels are **data**, not an enum — they live in `app_levels` and can be edited
without a code change. A person holds an app-wide level, and a level **per
project**, so the same person can be a lead on one engagement and a reviewer on
another.

| | Reach | What they can do |
|---|---|---|
| **owner** (`rian`) | everything | Immutable super admin. Synthesized by the accounts kit — not a row, cannot be demoted or removed. |
| **admin** | every project (`all_instances`) | Creates and manages projects, manages people and per-project levels, sees all results. Cannot mint another admin — only the owner can. |
| **lead** | one project | The client decision-maker: reviews **and** sees that project's results rollup. |
| **reviewer** | one project | Reviews only. Never sees anyone else's answers, or the rollup. |

Resolution order for a project: an explicit per-project grant wins → else the
app-wide level if it applies everywhere → else no access at all.

Identity still comes from BW Auth; levels are Scout's own and are mirrored to
`auth.bowden.works` for visibility only. Scout is the sole authority for its
assignments — there is no write-back from central.


## Not doing

- **No password store, no login form, no password reset.** Credentials live with BW Auth.
  Creating an account is `srv-gw id-user-create`; setting a password is
  `srv-gw id-user-set-password`. Scout never sees either.
- **No self-service signup.** Access is granted by an admin, always.
- **No inbound write-back.** Central can display Scout's access but never change it.
- **No service worker / PWA.** See `agents.md` for why that is a rule and not a
  preference.

## Known gaps

- **What has automated coverage:** the API boundary. 42 pytest tests run in-container
  against a real Postgres (`scout_test`) covering authz asymmetries (404-not-403,
  admin-route rejection, file-route membership), review autosave idempotence and
  validation, the whole import contract (zip-slip, schema_version, republish keeps
  reviews), and one full intake→report loop. `tsc` over the generated client checks the
  frontend/backend seam at every build.
- **What has no automated coverage:** the browser. The review game, admin tabs, and
  OAuth round-trip have been exercised only through the HTTP API and `tsc` — no
  Playwright suite, no human walkthrough of the new screens yet. The first real phone
  walkthrough of the review game is an open task.
- The test suite is not wired into any CI/deploy step — it runs via `docker cp` +
  `docker exec` (see `.logs/diary.md` 2026-08-12 for the exact invocation).
- Reviews never lock: "Finish" stamps completion but a reviewer can reopen and change
  answers indefinitely (deliberate for M1).
- Deactivating an account blocks its next request and removes it from the permission
  model entirely; it does not revoke the BW SSO session. Use `srv-gw id-logout --user
  <name>` when that matters.
- Permission resolution costs a few indexed queries per project in the list view. Fine at
  this scale; worth a per-request cache before the project count reaches the dozens.
- While the owner read-only-views an ADMIN target, the Options/Materials admin controls
  render clickable and rely on the middleware's VIEW_AS_READ_ONLY 403 instead of being
  disabled (reviewer-rank targets — the realistic case — are fully disabled).
- The sign-in screen's "Forgot password?" is a Scout-side stopgap; the right home is the
  central login page (feedback item G2).
- Invite email, reset-for-never-signed-in users, and the central account page's
  password-change capability are verified only up to the gateway call — the email legs
  await a human pass (handoff has the checklist).
