Skip to content

fix: install generated apps in isolated project roots with app-owned bun.lock files #180

Description

@artiphishle

Problem

Generated apps are currently created below apps/<slug>, while the Studio root declares:

"workspaces": [
  ".",
  "apps/*"
]

The Dashboard also runs bun install from the Studio repository root. As a result, every generated app becomes part of the Studio workspace installation and mutates the repository-level bun.lock.

This causes two architectural and workflow problems:

  1. Generated apps are not validated as truly standalone consumers. Their dependency graph may be satisfied through the Studio workspace installation and root-level package resolution.
  2. Local project creation or dependency updates dirty the Studio repository's committed bun.lock, mixing intentional Studio dependency updates with generated-user-project state that must not be committed.

The existing test named keeps generated apps outside Studio workspace installs currently expects ['.', 'apps/*'], which actually includes every generated app and therefore validates the opposite behavior.

Required solution

1. Restrict the Studio workspace to repository-owned packages

Change the root workspace declaration to an explicit allowlist:

"workspaces": [
  ".",
  "apps/studio"
]

Do not use apps/* and do not maintain dynamic exclusion entries for generated projects.

2. Install every generated app from its own project root

All install/update operations initiated by the Dashboard or host must execute with the generated project directory as cwd:

await runCommand({
  command: 'bun',
  args: ['install'],
  cwd: projectPath,
});

Equivalent CLI behavior:

bun install --cwd <projectPath>

Do not use a root install or workspace --filter, because both retain the shared Studio lockfile boundary.

3. Give each generated app its own installation state

Expected generated project layout:

<projectPath>/
├── package.json
├── bun.lock
├── node_modules/
├── app/
└── infra/

The Studio repository retains only its own bun.lock and node_modules.

4. Run project commands from the same project root

Expo start, dependency updates, type checks, and other app-owned commands must use cwd: projectPath. The Dashboard must never rely on root hoisting for generated apps.

5. Correct and strengthen tests

Update the current workspace test to require:

expect(packageJson.workspaces).toEqual(['.', 'apps/studio']);
expect(packageJson.workspaces).not.toContain('apps/*');

Add host/orchestrator tests asserting that generated app installation:

expect(spawnOptions.cwd).toBe(projectPath);
expect(spawnOptions.cwd).not.toBe(studioRoot);

Also verify that installing or updating a generated app does not modify the Studio root bun.lock, and that the generated app receives its own bun.lock.

6. Keep Studio Knip scoped to repository-owned workspaces

The current Studio Knip config already explicitly declares . and apps/studio; retain that scope. Generated apps should be linted/checked through app-owned commands, not included in Studio repository Knip discovery.

Acceptance criteria

  • Root workspaces contains only . and apps/studio.
  • Creating/installing/updating a generated app does not change the Studio root bun.lock.
  • Every generated app has its own bun.lock and dependency installation.
  • Generated app commands execute with cwd === projectPath.
  • Studio CI and Knip do not scan arbitrary generated apps.
  • A generated app can be copied outside the Studio checkout, installed, and launched without relying on Studio root node_modules.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions