Skip to content

Decompose the wizard store + session into composable, program-defined slices #768

Description

@gewenyu99

Problem

The global WizardSession and the TUI store have become a catch-all. Every program's state lives flat on one shared object, so the core keeps growing and infrastructure code carries product knowledge it shouldn't.

Today:

  • src/lib/wizard-session.ts404 lines, ~164 fields/members on the session interface.
  • src/ui/tui/store.ts944 lines, 29 explicit setters (each must remember to call emitChange()).
  • Program-specific state is flat on the shared session regardless of which program is running, e.g. mcpComplete / mcpOutcome / mcpInstalledClients / mcpSuggestedPromptsDismissed, slackStepDismissed / slackConnected, skillsComplete / skillId, … These exist on the session for an audit or revenue-analytics run that never touches MCP or Slack.

This violates the repo's own design discipline (AGENTS.md: product knowledge never enters infrastructure code). The store/session are machinery, but they currently know about MCP, Slack, skills, etc. Adding a program means widening the shared session and adding setters to the shared store — the bloat compounds.

Goal

Make the store and session composable and driven by program definitions (src/lib/programs/), so:

  • The core session holds only program-agnostic state (install dir, credentials, detected framework, run phase, current screen).
  • Each program contributes its own typed state slice (e.g. mcp, slack, skills) declared alongside its program definition, mounted only when that program runs.
  • The store composes setters/selectors from the active program's slices instead of hardcoding all 29.
  • Screens read/write their program's slice, not a flat global.

Sketch (not prescriptive)

  • A program definition declares an optional state: { initial, setters } slice with a typed shape.
  • buildSession() composes core + the active program's slices; the store derives its setter surface from them (so emitChange() is wired once, generically).
  • Migrate the obvious clusters first: mcp*, slack*, skill* → owned by the programs that use them.

Acceptance criteria

  • Core WizardSession no longer references program-specific concepts (MCP/Slack/skills/etc.).
  • At least the MCP, Slack, and skills state are program-owned slices, mounted only for programs that declare them.
  • Store setter/selector surface is composed from active slices, not a flat hardcoded list; emitChange() stays centralized.
  • Adding a new program needs no edits to the core session/store — only its own definition + slice.
  • pnpm build && pnpm test green; router/screen-resolution behavior unchanged.

Notes

  • Read .claude/skills/wizard-development/SKILL.md + references/ARCHITECTURE.md first — this is a structural change to the pipeline's core.
  • Keep the nanostore single-shallow-copy + explicit-setter invariant; the change is about where slices are defined and composed, not how mutation/emit works.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions