Skip to content

feat(e2e): let the e2e harness drive the warehouse flow - #1152

Merged
edwinyjlim merged 6 commits into
mainfrom
tom/wizard-e2e-warehouse-harness
Aug 27, 2026
Merged

edwinyjlim merged 6 commits into
mainfrom
tom/wizard-e2e-warehouse-harness

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

The warehouse flow has no pre-merge test coverage of the agent-in-the-loop layer. Unit tests cover the pure logic. Nothing exercises the wizard binary, the downloaded skill, the model and the PostHog MCP together. Every recent warehouse regression lived in that layer.

The e2e harness is the only thing that can drive the real TUI and the real agent. It cannot drive the warehouse flow today, because it runs a ci session and a ci session disables every ask. The warehouse flow is made of asks.

Changes

A harness-only escape hatch. shouldDisableAsk becomes (ci || signup) && !e2eAsk. e2eAsk is set only by the e2e host from E2E_ASK. No CLI flag reaches it and published builds cannot set it. Plain --ci behaviour is unchanged. The same change un-gates warehouseSeedTasks, which reads the identical predicate.

Whole ask batches, and task notices. The profile answered only questions[0] of a batch. Credential questions arrive in batches, so a warehouse run stopped at the first one. answerQuestions now answers every question. Resolution order per question: an askAnswers rule, else the first option, else the e2e sentinel. Overlay.TaskNotice had no action at all, so the seeded warehouse task could never be accepted; it gains resolve_notice, a taskNotice projection on CiState, and a notice profile policy.

decideE2eAction stays pure. resolveE2eProfile folds E2E_NOTICE and ${ENV_VAR} values into the profile once at load, so a decision never reads env or the store.

A richer result payload. E2E_RESULT_JSON reported screens walked and dependencies added. It said nothing about what the run was asked, what it answered, or what detection found. It gains asks, unansweredAsks, notices, tasks, detectedSources, reportFile and abort. Every pre-existing key is unchanged.

The host also writes the payload from an exit hook. An abort exits from inside the runner, so an aborted run wrote nothing at all — and the abort reason is exactly what an assertion needs in that case.

Security rail. Only question prompt text and question ids enter the payload. No answer value ever does. The decision function reports ids and a keep or decline verdict, so the recorder never holds an answer.

One thing worth a second look. vitest.config.ts excluded **/e2e-harness/** with a comment saying those tests lived on a stacked branch. That branch landed, so the whole harness suite has not been running. This removes the exclusion. Doing so immediately surfaced the missing task-notice action, which is the gap an exhaustiveness test exists to catch.

Test plan

  • 2347 tests pass, up from 2249 on main. The increase is the new tests plus the newly enabled harness suite.
  • tsc --noEmit output is byte-identical to a fresh origin/main worktree: 36 pre-existing errors before and after.
  • Prettier and ESLint clean; the 494 ESLint warnings are pre-existing and none is in a touched file.
  • New coverage: the shouldDisableAsk truth table; the seeded warehouse task queuing under ci + e2eAsk; batch answering, id-before-prompt precedence, case-insensitivity, malformed regex; ${ENV} interpolation including unset-to-sentinel; notice keep and decline including the E2E_NOTICE override; every screen in E2E_DRIVABLE_SCREENS yielding an action; and the security rail, which answers a real secret through the decision path and then scans the serialized payload for it.
  • End to end: the workbench branch below runs four warehouse fixtures through this harness. Detection runs before auth, so detection and signal-path checks were graded against real wizard output. The post-auth half needs a PostHog key and is not yet verified live.

Companion PRs: PostHog/wizard-workbench (warehouse fixtures, stub MCP and assertions) and the ref-pinning PRs in this repo and PostHog/context-mill.

A `ci` session disables the ask bridge, because no person is there to
answer. The e2e harness is the exception: it runs a `ci` session, but its
driver loop answers each batch from the program's profile. Without an
escape hatch the agent-in-the-loop layer has no pre-merge test at all.

`session.e2eAsk` re-enables the bridge. Only the e2e TUI host sets it,
from the `E2E_ASK` env var. No CLI flag populates it, so plain `--ci` and
`--signup` runs behave exactly as before.

The flag also un-gates the orchestrator's seeded warehouse task, which
`shouldDisableAsk` gates through the same policy.
…house profile

The profile could answer only `questions[0]` of a `wizard_ask` batch, and
the task-notice overlay had no action at all. Both stop a warehouse run
dead: credential questions arrive in batches, and the seeded warehouse
task opens a notice first.

- `answerQuestions` answers every question in the batch. Resolution order
  per question: an `askAnswers` rule, else the first option, else the
  `'e2e'` sentinel. A rule matches on the question id first, then on the
  prompt. A `multi` question gets an array answer.
- `resolveE2eProfile` folds `E2E_NOTICE` and `${ENV_VAR}` values into the
  profile at load. `decideE2eAction` therefore stays pure: it reads no
  env and no store, so the flow-snapshot test does not depend on the
  shell it runs in.
- `Overlay.TaskNotice` gets a `resolve_notice` action, a `taskNotice`
  projection on `CiState`, and a `notice` profile policy. The projection
  carries title, items and prompt only.
- The warehouse-source program declares its e2e profile, routing each
  credential question to its own `E2E_*` env var.

The e2e-harness tests were excluded from vitest while the harness lived
on a stacked branch. It landed, so run them. That surfaces the gap the
task-notice action closes.
…yload

`E2E_RESULT_JSON` reported the screens walked and the dependencies added.
It said nothing about what the run was asked, what it answered, or what
detection found — the facts a warehouse assertion needs.

The payload gains `asks`, `unansweredAsks`, `notices`, `tasks`,
`detectedSources`, `reportFile` and `abort`. Every pre-existing key is
unchanged.

Security rail: only question prompt text and question ids enter the file.
No answer value ever does. The decision function reports ids and a
keep/decline verdict, so the recorder never holds an answer. A test
injects a secret through the real decision path and scans the serialized
payload for it.

The host also writes the payload from an `exit` hook. An abort exits from
inside the runner, so an aborted run used to write nothing at all — and
the abort reason is exactly what an assertion needs in that case.
@Gilbert09
Gilbert09 requested review from a team as code owners August 27, 2026 14:30
@github-actions

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 ai-observability
  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/groq
  • /wizard-ci ai-observability/manual-capture
Show more apps
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/vercel-ai
  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/flutter
  • /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 replay-vision/javascript-node
  • /wizard-ci replay-vision/next-js
  • /wizard-ci replay-vision/react-vite
  • /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.

Comment thread e2e-harness/e2e-profile.ts Resolved
Comment thread e2e-harness/e2e-result.ts Resolved
@veria-ai

veria-ai Bot commented Aug 27, 2026 •

Copy link
Copy Markdown

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 2 · PR risk: 0/10

@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.

addressed veria findings

@edwinyjlim
edwinyjlim merged commit 0b87fc8 into main Aug 27, 2026
17 checks passed
@edwinyjlim
edwinyjlim deleted the tom/wizard-e2e-warehouse-harness branch August 27, 2026 23:31
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.

2 participants