Conversation
The snapshot route and the wizard-ci MCP server spawn the real binary with `--ci --control-socket` in a PTY and drive it through the control API: the run is released with POST /run, state is long polled, and every commit goes through POST /actions. The in-process host, its store driver, and the action registry are gone; the harness keeps the launcher, the detection picks, the profiles, and the result payload. Harness tests run against the store's ControlDriver and per-flow actions with unchanged goldens, and two process specs exercise each surface end to end. Docs describe the API and the launcher; the workbench environment contract is unchanged. Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
self-driving, upload-source-maps, doctor, and skill branched on --ci themselves and never saw the control socket. They dispatch through dispatchProgram now, so --ci with --control-socket drives the real TUI for every command, and self-driving refuses --ci only when no parent is attached. Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
🧙 Wizard CIRun 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:
Test all apps in a directory:
Test an individual app:
Show more apps
Test against a Context Mill branch:
Add Results will be posted here when complete. |
A failed run parks the TUI on the handoff screen until someone chooses. The fixed profile now exits from it and ends the walk, so the result payload carries the abort reason instead of the run waiting out its timeout. Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
Harness routes: why each piece existsTop to bottom is the direction of a run. The harness never touches the TUI's input: it commits over the socket and reads the frame back through the PTY. flowchart TB
classDef wave_entry fill:#fff8e6,stroke:#d9a400,stroke-width:2px,color:#d9a400;
classDef card_entry fill:#fff1c7,stroke:#d9a400,stroke-width:1px,color:#1d1d1f;
classDef wave_cli fill:#fff1e8,stroke:#e8632c,stroke-width:2px,color:#e8632c;
classDef card_cli fill:#ffe1cc,stroke:#e8632c,stroke-width:1px,color:#1d1d1f;
classDef wave_tui fill:#e9f0ff,stroke:#2f6fed,stroke-width:2px,color:#2f6fed;
classDef card_tui fill:#d6e4ff,stroke:#2f6fed,stroke-width:1px,color:#1d1d1f;
classDef wave_agent fill:#f1e9ff,stroke:#7a3fe0,stroke-width:2px,color:#7a3fe0;
classDef card_agent fill:#e6d8ff,stroke:#7a3fe0,stroke-width:1px,color:#1d1d1f;
classDef wave_store fill:#e8f8ee,stroke:#1f9d55,stroke-width:2px,color:#1f9d55;
classDef card_store fill:#d4f2df,stroke:#1f9d55,stroke-width:1px,color:#1d1d1f;
classDef wave_control fill:#e4f5f3,stroke:#148f7a,stroke-width:2px,color:#148f7a;
classDef card_control fill:#cfeeea,stroke:#148f7a,stroke-width:1px,color:#1d1d1f;
classDef wave_harness fill:#f2f2f2,stroke:#6e6e73,stroke-width:2px,color:#6e6e73;
classDef card_harness fill:#e6e6e6,stroke:#6e6e73,stroke-width:1px,color:#1d1d1f;
classDef wave_guard fill:#fbe9e7,stroke:#c0392b,stroke-width:2px,color:#c0392b;
classDef card_guard fill:#f8d7d2,stroke:#c0392b,stroke-width:1px,color:#1d1d1f;
subgraph W1["ROUTES: prove the real TUI runs end to end without a keyboard"]
direction LR
W1C1("<b>tui-snapshots.no-jest.ts</b><br/>the real TUI in a PTY; a fixed profile decides<br/>each screen; frames become the evidence")
W1C2("<b>wizard-ci-mcp.no-jest.ts</b><br/>a coding agent decides each screen through<br/>MCP tools; same socket, same TUI")
W1C3("<b>controlled-headless-smoke.no-jest.ts</b><br/>the published surface: detect, independent<br/>runs, the ledger, a clean exit")
W1C4("<b>control-socket-*.test.ts</b><br/>the real binary on both surfaces in CI,<br/>no credentials, no agent run")
W1C1 ~~~ W1C2 ~~~ W1C3 ~~~ W1C4
end
class W1 wave_harness
class W1C1,W1C2,W1C3,W1C4 card_harness
subgraph W2["e2e-harness: the parent's side, pure where it decides, real where it launches"]
direction LR
W2C1("<b>launch.ts</b><br/>starts the real binary from each program's<br/>own command; waits for the server to answer")
W2C2("<b>e2e-profile.ts + profiles.ts</b><br/>pure policy: screen in, commit out;<br/>no env, no store, no socket")
W2C3("<b>picks.ts</b><br/>computes what a picker screen would ask<br/>a human, then commits it as an action")
W2C4("<b>e2e-result.ts</b><br/>records question ids and prompts,<br/>never an answer value")
W2C5("<b>tui-capture.ts</b><br/>reads the frame back through the PTY;<br/>never types into it")
W2C1 ~~~ W2C2 ~~~ W2C3 ~~~ W2C4 ~~~ W2C5
end
class W2 wave_harness
class W2C1,W2C2,W2C3,W2C4,W2C5 card_harness
subgraph W3["THE WIZARD PROCESS: unchanged code, driven from outside"]
direction LR
W3C1("<b>src/cli</b><br/>picks the surface: controlled TUI<br/>or headless, from the same flags")
W3C2("<b>src/store/control</b><br/>the store on a socket (P4)")
W3C3("<b>src/store FlowStore + RunStore</b><br/>one flow, one store per run; the TUI and<br/>the socket read and commit the same one")
W3C4("<b>src/tui</b><br/>the real Ink render, reacting to<br/>every commit, captured as frames")
W3C1 ~~~ W3C2 ~~~ W3C3 ~~~ W3C4
end
class W3 wave_store
class W3C1,W3C2,W3C3,W3C4 card_store
subgraph W4["EVIDENCE: what a reviewer or the workbench reads"]
direction LR
W4C1("<b>NN-<screen>.ans</b><br/>one frame per screen change or task update,<br/>colors kept")
W4C2("<b>E2E_RESULT_JSON</b><br/>run phase, screen path, asks, notices, tasks,<br/>abort reason: what happened, never how it was answered")
W4C3("<b>GET /runs</b><br/>each independent run and the state it ended in")
W4C1 ~~~ W4C2 ~~~ W4C3
end
class W4 wave_guard
class W4C1,W4C2,W4C3 card_guard
W1 -->|"Launch = buildLaunch(...) the key in env only, outer agent credentials stripped<br/>ControlClient long-poll GET /state · POST /actions · POST /run · POST /credentials"| W2
W2 -->|"every decision enters as one control action; every frame leaves through the PTY"| W3
W3 -->|"frames · result payload · MCP replies · run ledger"| W4
Interfaces, one sheet eachHow to run this step# Snapshot route: spawns the real binary in a PTY, drives the fixed profile over the socket
PROGRAM=posthog-integration E2E_ASK=true \
POSTHOG_KEY_FILE=/path/to/phx-key.txt WIZARD_CI_GATEWAY_TOKEN_FILE=/path/to/token.txt \
PROJECT_ID=<id> POSTHOG_REGION=us APP_DIR=/tmp/app \
SNAP_OUT=/tmp/snaps E2E_RESULT_JSON=/tmp/snaps/result.json \
npx tsx scripts/tui-snapshots.no-jest.ts
# MCP route: the wizard-ci server from .mcp.json, driven by the explore script
APP_DIR=/tmp/app PROJECT_ID=<id> POSTHOG_KEY_FILE=/path/to/phx-key.txt npx tsx scripts/wizard-ci-explore.no-jest.ts
# Process specs: the real binary on both surfaces, no credentials, no agent run
pnpm test:harness # WIZARD_PTY_TESTS=0 skips the PTY specThe workbench environment contract for Evidence: basic integration through the snapshot path (socket route)Run on 2026-09-19 against a throwaway copy of
{
"runPhase": "completed",
"screenPath": [
"intro",
"auth",
"run",
"outro",
"mcp",
"slack-connect",
"keep-skills"
],
"hasPosthogDep": true,
"newDeps": [
"posthog-node"
],
"skillsComplete": false,
"unansweredAsks": 0,
"refusedAsks": 0,
"abort": null,
"tasks": [
"completed:Add PostHog SDK",
"completed:Initialize PostHog",
"skipped:Track user identity",
"completed:Capture unhandled errors",
"completed:Instrument key events",
"completed:Review integration",
"completed:Create product dashboard",
"completed:Report integration"
]
}Against the Frames (16)Intro frame (ANSI stripped)Outro frame (ANSI stripped)Re-run after the control state became a store mirror ( Evidence: MCP route (
|
Review of the stack: what changed, what is left, what to checkFive independent reviews ran over Fixed in this stack
Left as is, for you to decide
Exceptions to check in the PRCross-surface edges that exist on purpose:
|
# Conflicts: # scripts/README.md # src/store/control/__tests__/state.test.ts # src/store/control/state.ts # src/store/control/types.ts
Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
The harness architecture doc gains the commands for the snapshot route, the MCP route, the controlled headless smoke, and the process specs; AGENTS.md points at it. The store boundary constant matches this tree's usage. Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
The routes, the profile, the recorder, and the process specs read session.runPhase, session.pendingQuestion, session.taskNotice, and the redacted session.frameworkContext where the control state now mirrors the store; the MCP proxy derives its integration status from the run phase. Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
…rect the docs launchWords reads each program's command instead of a second table; waitForSocket waits for the server to answer; the headless smoke script shares buildLaunch, readApiKey, and the APP_DIR/PROJECT_ID/POSTHOG_KEY_FILE contract; the snapshot route rethrows client errors instead of treating them as an exit; the MCP run status lives in run-status.ts with a test. Keyboard equivalence asserts equality per pair and names the next screen's mount effects; E2E_DRIVABLE_SCREENS is derived from decideE2eAction; picks, the route table, and the doctor dispatch gain tests; dead fixtures go. Docs drop the stale aliases, the router-as-resolver claim, the bin.ts registration, InkUI, and the .txt frames, and list @store/control. Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
Snapshot frames: every profiled program on this branchEach program ran once through posthog-integration: 16 frames captured, 8 shown · runPhase completedScreen path: 01-intro 02-auth 03-run 12-run 13-outro 14-mcp 15-slack-connect 16-keep-skills self-driving: 41 frames captured, 10 shown · runPhase completedScreen path: 01-self-driving-intro 02-self-driving-integration-check 03-auth 04-self-driving-integration-detect 05-run 21-self-driving-handoff 22-self-driving-github 32-wizard-ask 40-run 41-outro metrics: 9 frames captured, 6 shown · runPhase completedScreen path: 01-metrics-intro 02-auth 03-run 07-run 08-outro 09-keep-skills replay-vision: 13 frames captured, 6 shown · runPhase completedScreen path: 01-agent-skill-intro 02-auth 03-run 11-run 12-outro 13-keep-skills ai-observability: 14 frames captured, 6 shown · runPhase completedScreen path: 01-ai-observability-intro 02-auth 03-run 12-run 13-outro 14-keep-skills error-tracking: 19 frames captured, 8 shown · runPhase completedScreen path: 01-error-tracking-intro 02-auth 03-error-tracking-detect 04-run 07-wizard-ask 17-run 18-outro 19-keep-skills error-tracking-upload-source-maps: 19 frames captured, 8 shown · runPhase completedScreen path: 01-source-maps-intro 02-auth 03-source-maps-detect 04-run 06-wizard-ask 17-run 18-source-maps-outro 19-keep-skills warehouse-source: 6 frames captured, 6 shown · runPhase completedScreen path: 01-warehouse-intro 02-auth 03-run 04-run 05-outro 06-keep-skills |
The bundle audit compares these with the manifests CI builds; the new modules and the regrouped chunks belong in them. A failed comparison now uploads its manifests and reports both files. Generated-By: PostHog Desktop Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
















































Drive the snapshot route and the wizard-ci MCP server over the control socket.
Both spawn the real binary with
--ci --control-socketin a PTY; the in-process host, store driver, and action registry are gone. Harness tests run against the store'sControlDriverwith unchanged goldens, two process specs cover each surface, and a real sweep of eight programs matched the old route on every transition frame. Stacked on #1277.Created with PostHog Desktop