Skip to content

feat(self-driving): wire up the Replay Vision scanners step - #1055

Open
Twixes wants to merge 2 commits into
mainfrom
feat/self-driving-replay-vision-scanners
Open

feat(self-driving): wire up the Replay Vision scanners step#1055
Twixes wants to merge 2 commits into
mainfrom
feat/self-driving-replay-vision-scanners

Conversation

@Twixes

@Twixes Twixes commented Aug 4, 2026

Copy link
Copy Markdown
Member

Human tl;dr: Pairs with PostHog/context-mill#313. This adds the scaffolding for the new replay vision setup step in scanner. Scoping changes are key, then a little bit of text updates.

What

The wizard glue for a new self-driving step 6c: create Replay Vision scanners with emits_signals: true so on-screen breakage a recording reveals reaches the inbox, alongside what sources and scouts already pull. Three small surfaces, since the HOW lives in the skill:

  • program-scopes.tsreplay_scanner:read / replay_scanner:write.
  • prompt.ts — STEP 6c + its task-list entry.
  • content/tips.ts — a Scanner tip.

The scope name is the thing to get right

The scope object is replay_scanner, not vision-scanners-*. Those are MCP tool names — requesting one grants nothing and the step 403s on every call. (ReplayScannerViewSet.scope_object.)

Create/update also require session_recording:read alongside replay_scanner:write — the API pairs them via dangerously_get_required_scopes, since a scanner's config indirectly exposes recording contents. That one is already granted for the step-2 usage probes, so only the replay_scanner pair is net-new to the requested set.

No prod OAuth-ceiling edit is needed — this corrects an earlier version of this PR (and some stale repo docs, fixed in the second commit). The live wizard apps' OAuthApplication.scopes use the @default sentinel — US prod is @default,llm_gateway:read,wizard_session:read,wizard_session:write. @default resolves to UNPRIVILEGED_SCOPES (every public non-privileged/non-internal/non-hidden scope) and auto-tracks new ones, so replay_scanner:* is already inside the ceiling. Verify with python manage.py seed_oauth_app_scopes --client-id <id> --scopes @default,… --dry-run on the posthog side. A ceiling edit is only ever needed for a privileged/internal/hidden object, which this isn't.

Not a blocker either: the endpoints sit behind the replay-vision flag and 404 when off, but its first release condition is properties: [], rollout_percentage: 100 — everyone. The skill still treats a 404 as a recorded follow-up.

What the prompt carries

Only what the wizard owns — order, the never-abort contract, and the two constraints too load-bearing to leave to the skill alone: don't edit the skeletons' locked fields, and keep the scanners' queries disjoint.

That second one is documented in ARCHITECTURE §10.3 and is the non-obvious part of this whole change. emits_signals appends a fixed defect turn to every scan, identical regardless of the scanner's own prompt — so prompts don't differentiate scanners, queries do. And since grouping matches signals semantically rather than by source, two scanners over the same sessions describe one defect twice and sum 0.5 + 0.5 to exactly WEIGHT_THRESHOLD. Overlapping scanners self-promote, defeating the half-weight design that's meant to require corroboration.

Sequencing

  1. Merge this and cut the wizard npm release.
  2. Then merge context-mill#313 with mcp-publish.

Reversed, the agent reads step 6c before the release ships the requested scope, and 403s. Note the fix there is a reconnect (refresh tokens keep their original grant), not a ceiling edit — nothing manual on the prod OAuth app. Everything here is inert until the skill ships, so this half is safe to land first.

Testing

pnpm build && pnpm test && pnpm lint — 1719 pass, 0 lint errors. Three new tests: STEP 6c present and ordered 6b → 6c → 7, its task-list entry exists (a STEP with no task silently drops off the TUI progress view), and the scope additions cover replay_scanner:* + session_recording:read without weakening the base set.

The wizard glue for step 6c, whose HOW lives in the context-mill skill:
the OAuth scope, the prompt STEP + task-list entry, and a sidebar tip.

The scope object is `replay_scanner`, not `vision-scanners-*` — those are
MCP tool names, and requesting one grants nothing, so the step would 403 on
every call. Create/update also require `session_recording:read` alongside
`replay_scanner:write` (the API pairs them, since a scanner's config
indirectly exposes recording contents); that one is already granted for the
step-2 usage probes, so only the `replay_scanner` pair is net-new. Like
`product_enablement:write`, it needs the manual prod OAuth-ceiling edit
before a token can carry it — README and the §7 checklist updated.

The prompt carries only what the wizard owns: order, the never-abort
contract, and the two constraints too load-bearing to leave to the skill
alone — don't edit the skeletons' locked fields, and keep the scanners'
queries disjoint. ARCHITECTURE §10 documents why that second one matters:
overlapping scanners describe one defect twice and self-corroborate past the
promotion threshold.

The scanner tip earns its slot because a scanner is the least familiar
thing in the run — an LLM watching recordings on a cadence, and the only
part of setup that spends Replay Vision quota.
@Twixes
Twixes requested review from a team as code owners August 4, 2026 04:25
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci revenue
  • /wizard-ci self-driving

Test an individual app:

  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
Show more apps
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit

Results will be posted here when complete.

… scopes

The live wizard OAuth apps' `OAuthApplication.scopes` use the `@default`
sentinel (US prod: `@default,llm_gateway:read,wizard_session:read,
wizard_session:write`), not the exhaustive literal list this repo's docs
described. `@default` resolves to `UNPRIVILEGED_SCOPES` — every public
(non-privileged, non-internal, non-hidden) obj:action scope — and
auto-tracks new ones (`posthog/scopes.py`, `resolve_ceiling`).

So `replay_scanner:*` — and `product_enablement:write`, and every other
self-driving addition — are already inside the ceiling. There is **no
manual prod ceiling edit** for any of them; I had it wrong, and so did the
README (§ "OAuth app scope ceiling"), ARCHITECTURE §3/§7/§9/§10. Only a
privileged/internal/hidden object would ever need a per-app edit, and the
wizard requests none.

Rewrites the README ceiling section to explain `@default` and how to verify
(`seed_oauth_app_scopes --dry-run`), flips §7 item 1 from "add ten net-new
objects" to "no action needed, here's why + how to check", and drops the
"one manual step outstanding" banners from §9 (product_enablement) and §10
(replay_scanner). The only real ordering constraint left is wizard-release-
before-mcp-publish, and a token predating the scope is fixed by a reconnect,
not a ceiling edit.

Also notes the EU prod app: a per-region DB row referenced via
`WIZARD_CLOUD_RUN_OAUTH_CLIENT_ID`, not committed here, seeded the same
`@default,…` way.

@edwinyjlim edwinyjlim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm but will let someone from self-driving verify behavior

@posthog

posthog Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🦔 ReviewHog reviewed this pull request

Nothing worth raising this time, so here's a calming picture instead:

A panda relaxing and waving

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants