Skip to content

refactor: give each program a tui entry and end runs through the store - #1382

Closed
gewenyu99 wants to merge 1 commit into
posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session-from
posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru
Closed

gewenyu99 wants to merge 1 commit into
posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session-from
posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru

Conversation

@gewenyu99

@gewenyu99 gewenyu99 commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Phase 2 of Release C (#1320), PR 6 of 16, after #1381. Review PR. PRs 1 to 14 of this phase split one change into reviewable pieces and compile only as a stack: review them one by one, then land the whole phase together.

Problem

Screens on main call process.exit and read TUI answers from store.session, and a program's screens, deck and tips hang off its ProgramConfig. So the TUI core names every program, and a screen can end the process under its host.

Changes

  • Each program's TUI lives in its folder's entry, src/tui/programs/<id>/index.tsx: its flow, screens, deck and tips (TuiProgram in programs/types.ts). getTuiProgram and flowOwner find them, so the core names no program.
  • Screen ids move to leaf modules: src/tui/screen-ids.ts for the core (its code moved in refactor: move every file into its layer #1375), programs/<id>/screen-ids.ts for each program.
  • Every process.exit in a screen becomes store.requestExit(code) with the same code, for example in AuthErrorScreen, ExitScreen, ManagedSettingsScreen, PortConflictScreen and HealthCheckScreen.
  • Screens read TUI answers from the store instead of the session, for example store.slackConnected, store.settingsConflicts and store.mintHandoff.
  • New helpers: abortOnScreens (abort.ts) shows an abort on the outro screen, renderFamilyPicker (family-picker.tsx) brings the CLI's subcommand picker into the TUI, services/slack-app-card.ts (moved in refactor: move every file into its layer #1375) now holds the Slack card copy itself instead of importing it from the MCP prompts, and startPlayground resolves an exit code.
  • harness.tsx holds what the e2e harness takes from the TUI in its own process: createTuiStore (a store with no terminal), mountScreens (the real screens on a test terminal), readTuiState and tuiScreenIds. The TUI entry loads it on first use in refactor: add the tui and headless hosts and let only the cli exit #1383.
  • Focus on the process.exit swaps and each program's index.tsx. Most other edits read a field from its new home.

Test plan

Created with PostHog Desktop

@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 feature-flags
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving
  • /wizard-ci warehouse
  • /wizard-ci warehouse-seeded

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/google-adk
  • /wizard-ci ai-observability/groq
Show more apps
  • /wizard-ci ai-observability/manual-capture
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/opentelemetry
  • /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 feature-flags/django
  • /wizard-ci feature-flags/next-js
  • /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
  • /wizard-ci warehouse/monorepo-env
  • /wizard-ci warehouse/multi-source-next
  • /wizard-ci warehouse/stripe-node
  • /wizard-ci warehouse/zero-source
  • /wizard-ci warehouse-seeded/next-stripe
  • /wizard-ci warehouse-seeded/next-stripe-declined

Test against a Context Mill branch:

  • /wizard-ci all context-mill:my-branch

Add context-mill:<branch> to any command above to pin the Context Mill branch. It defaults to main.

Results will be posted here when complete.

@gewenyu99
gewenyu99 added this pull request to stack #1394 September 29, 2026 09:41
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session- branch from 3521f0c to e5138a9 Compare September 30, 2026 08:06
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru branch from 4c97205 to cc1f5dd Compare September 30, 2026 08:06
@gewenyu99
gewenyu99 removed this pull request from stack #1394 September 30, 2026 08:07
@gewenyu99
gewenyu99 added this pull request to stack #1400 September 30, 2026 08:07
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session- branch from e5138a9 to fd7ec0e Compare September 30, 2026 08:44
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru branch from cc1f5dd to e321c28 Compare September 30, 2026 08:44
@gewenyu99
gewenyu99 marked this pull request as ready for review September 30, 2026 09:18
@gewenyu99
gewenyu99 requested review from a team as code owners September 30, 2026 09:18
@gewenyu99
gewenyu99 requested review from ablaszkiewicz and hpouillot and removed request for a team, ablaszkiewicz and hpouillot September 30, 2026 09:18
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru branch from e321c28 to 9ce4be1 Compare September 30, 2026 14:11
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session- branch from fd7ec0e to 2b4fdc6 Compare September 30, 2026 14:11
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru branch from 9ce4be1 to 38c058c Compare October 1, 2026 06:43
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session- branch from 2b4fdc6 to 62de363 Compare October 1, 2026 06:43
@gewenyu99
gewenyu99 removed this pull request from stack #1400 October 1, 2026 06:47
@gewenyu99
gewenyu99 added this pull request to stack #1403 October 1, 2026 06:47

@johncwaters johncwaters left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

Automated review. Not written by a human.

  • src/tui/tools/doctor/screens/DoctorIntroScreen.tsx:48: Cancel still calls process.exit(0), which the description says every screen stopped doing. Use store.requestExit(0), unless a later PR in the stack handles it.

// After a mint failure run-wizard owns the exit (status 1, analytics).
if (!store?.session.mintHandoff) process.exit(0);
// After a mint failure the host owns the end (status 1, analytics).
if (!store.mintHandoff) store.requestExit(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

Automated review. Not written by a human.

At this head no real host (start-tui.ts, run-wizard.ts, the slack and mcp commands) subscribes to store.exitRequest, and nothing calls store.launch(). This exit and the other converted ones (AuthError, PortConflict, ManagedSettings, intro Cancels, KeepSkills, the health-check outage abort) would leave the TUI hanging, and screens reading store.mcpFeatures, store.programLabel or integrate would see null. Fine if a later PR in the stack wires the hosts before the phase lands. Which PR does that?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Expected at this PR: the hosts that subscribe to exitRequest and call store.launch() land in #1383 (src/tui/run.ts, src/tui/run-tool.ts).

unmount();
releaseTerminal();
process.exit(0);
return new Promise((resolve) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

Automated review. Not written by a human.

This dropped its process.exit(0) and now returns a promise, but runPlayground in src/cli/commands/basic-integration/playground.ts still ignores it. Any handle left open keeps wizard playground from returning to the shell. Could runPlayground await it and set process.exitCode?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Handled in #1385: src/cli/commands/basic-integration/playground.ts runs it through exitWith, which exits with the resolved code, so open handles can't hold the shell.

@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru branch from 38c058c to fabac48 Compare October 2, 2026 16:47
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session- branch from 62de363 to 0ecdbe0 Compare October 2, 2026 16:47
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru branch from fabac48 to f8b95db Compare October 2, 2026 20:50
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session- branch from 0ecdbe0 to 7597ed2 Compare October 2, 2026 20:50
Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-08-put-the-tui-store-on-top-of-the-session- branch from 7597ed2 to 581e05a Compare October 3, 2026 20:40
@gewenyu99
gewenyu99 force-pushed the posthog/functional-r9-09-give-each-program-a-tui-entry-and-end-ru branch from f8b95db to 11b9caa Compare October 3, 2026 20:40

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

Review of #1382 at 11b9caab. Comments inline.


Drafted with Claude Code from a review of the whole Release C stack, re-verified at the stack tip 2e31e438. Line numbers are at this PR's head.

? () => void handleDownloadAndExit()
: () =>
void wizardAbort({
void abortOnScreens(store, {

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.

[P1] The health-check outage "Exit" never ends the run: the abort's outro cannot render, so abortOnScreens waits forever

The Exit handlers call abortOnScreens(store, { code: EnvServiceOutage }). The presenter sets the error outro and waits for outroDismissed, but the router walk stops at the health-check step because its readiness predicate stays false, and the router's only abort special case is the auth entry (src/tui/router.ts:82). So the screen stays health-check, the outro never renders, and nothing ends the exit. A probe at the stack tip (2e31e43) after the abort reads currentScreen = health-check, runPhase = error, exitRequest = null. The only way out is Ctrl-C, which reports 130 and "cancelled" instead of the outage error. Main had the same shape; this PR rewrites the handler and says screens now end runs through the store, but this path still can't. Same at lines 198 and 204.

Suggested fix: Let the walk reach the outro once an abort is recorded: in router.resolve generalize the special case to if (session.runPhase === RunPhase.Error && session.outroData) return ScreenId.Outro before return entry.id; auth and health-check both need it, and real run failures are already diverted earlier. Or have the Exit handler call store.requestExit(1) after analytics.shutdown('error') instead of the presenter. Add a screen-exits case for the outage Exit asserting exitRequest === 1.

@gewenyu99

Copy link
Copy Markdown
Collaborator Author

Folded into #1378 so the phase builds green PR by PR. This PR keeps its review; the code lands through #1378.

@gewenyu99 gewenyu99 closed this Oct 5, 2026
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