Skip to content

fix(fix): discover independent nested projects (UPT-120) - #258

Merged
lucasbraum merged 2 commits into
mainfrom
fix/upt-120-nested-project
Oct 19, 2026
Merged

lucasbraum merged 2 commits into
mainfrom
fix/upt-120-nested-project

Conversation

@lucasbraum

Copy link
Copy Markdown
Collaborator

What and why

Fixes UPT-120. Running uptide fix vitest at a repository root with only tooling dependencies now discovers the independent frontend/ project that declares Vitest and has its own lockfile. CLI settings and preflight, installation, migration, verification and the resulting branch all use that project.

Existing root/workspace selection takes precedence. Discovery respects Git ignores and skips dependencies, generated/hidden directories and symlinks. Multiple matching projects produce an error naming their manifests and asking for an explicit --cwd; a workspace member's stray lockfile does not make it independent.

How it was verified

  • Reproduced the original refusal on uptide-bench/ZM-BAD--DAG-chat at 340e0b0996cf380483a320071f5b5e4d8d27340c.
  • Ran the built CLI from that repository root with fix vitest --target 5.0.5 --yes --json and the internal rule-only switch (UPTIDE_INTERNAL=1 --no-llm, no API key). The migration completed: 178 tests in 8 files passed before and after, type checks and tsc && vite build passed, and only frontend/package.json and frontend/package-lock.json changed. The original checkout was unchanged; no benchmark PR was published.
  • Added an end-to-end fixture that failed before the fix and now verifies nested install/test paths, the exported migration branch, untouched root files, and source-checkout isolation. Added discovery and CLI configuration regressions. All 55 focused core tests passed.
  • pnpm build, pnpm lint, pnpm typecheck, pnpm test, and pnpm docs:links.

Checklist

  • Tests for the change (a bug fix starts with a failing test)
  • pnpm lint && pnpm typecheck && pnpm test pass
  • README and docs updated if behavior changed
  • Changeset added if a user would notice (pnpm changeset)
  • No new dependency, or it was agreed in an issue
  • No private code, names or paths in fixtures, tests or docs
  • The rules in CONTRIBUTING.md ("Rules that do not bend") still hold

Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
@tenki-reviewer

tenki-reviewer Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Review complete. 🟡 1 medium

💬 Inline comments (1)

  • 🟡 Broken nested manifests abort dependency discovery — projects.ts:33
🧹 Nitpicks (1) — 🟢 1 low
  • 🟢 Relative --config resolves against relocated project directory (cli.ts:1037) — settings(cwd, flags.config) now runs after projectForDependency reassigns cwd (packages/cli/src/cli.ts:1037), and loadUptideConfig resolves an explicit config relative to that cwd (packages/core/src/onboarding/node.ts:25).

This change teaches uptide fix to detect independent nested projects below the repository root and re-anchor the run to the closest matching project directory, instead of only considering the invocation root. CLI failure reporting and core exports were extended to support the new discovery path, and tests cover nested-project isolation and project resolution.

Files Change
packages/core/src/projects.ts, packages/core/src/index.ts Add nested-project discovery (projectForDependency) and export it from core, walking nested manifests and workspace patterns.
packages/core/src/fix/process.ts, packages/core/src/fix/isolate.ts Use the discovered project root to drive fix/isolate processing, including cwd relocation semantics.
packages/cli/src/cli.ts, packages/cli/src/failures.ts Wire discovery into the CLI fix command and extend failure reporting for the new paths.
packages/cli/src/nested-project.test.ts, packages/core/src/fix/isolate.test.ts, packages/core/src/projects.test.ts Add tests for nested-project discovery, isolation, and resolution behavior.
README.md, .changeset/tidy-nested-projects.md Document the new behavior and add a patch changeset.

Reviewed commit: 65b75f6

@tenki-reviewer tenki-reviewer Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The PR adds discovery of nested independent projects during uptide fix, relocating the run to the matched project, with new tests and a changeset.

Key findings

  • 🟡 Broken nested manifests abort dependency discovery — projects.ts:33

Comment on lines +33 to +40
const pkg = JSON.parse(readFileSync(file, 'utf8'));
return names.some((name) =>
['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'].some(
(field) => Object.hasOwn(pkg[field] ?? {}, name),
),
);
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 bug · medium

Broken nested manifests abort dependency discovery

The new discovery walk probes arbitrary directories, but parses them with strict, unguarded calls: declares runs JSON.parse on every workspace member manifest with no error handling (packages/core/src/projects.ts:33), and the walk resolves nested workspaces with the strict workspacePackagesOf, which throws INVALID_WORKSPACE for a broken pnpm-workspace.yaml (packages/core/src/projects.ts:79). One malformed nested package.json (or one whose JSON is not an object, which also throws TypeError at the field access) or a broken workspace file therefore makes projectForDependency throw a raw SyntaxError, TypeError, or INVALID_WORKSPACE instead of returning a project, failing the whole fix run before any error reporting. The codebase already guards the root manifest in declaredPatterns and exports the tolerant workspacePackagesOrRoot for exactly these probe contexts, but this walk uses neither.

📋 Prompt for AI Agents

In packages/core/src/projects.ts, harden the discovery probes: (1) in declares (lines 29-40), wrap readFileSync/JSON.parse in a try/catch that treats an unreadable or invalid manifest as not declaring the dependency, and guard that the parsed value is a non-null object before the field access; (2) replace workspacePackagesOf with workspacePackagesOrRoot from './workspaces.js' for the directories probed during the walk (the declares helper, the workspacePackagesOf(child) call at line 79, and the seed call at line 85), and in isIndependentProject (lines 20-27), which is called from projectRoot (packages/core/src/fix/process.ts:127) and the ancestor loop in packages/cli/src/failures.ts:204. This prevents a malformed vendored/example package.json or a broken nested pnpm-workspace.yaml from aborting uptide fix with a raw error before the intended NO_PROJECT/NOT_REPOSITORY_ROOT diagnostics.

@lucasbraum
lucasbraum merged commit 3a4898f into main Oct 19, 2026
10 of 11 checks passed
@lucasbraum
lucasbraum deleted the fix/upt-120-nested-project branch October 19, 2026 16:36
@uptide-release uptide-release Bot mentioned this pull request Oct 19, 2026
lucasbraum pushed a commit that referenced this pull request Oct 19, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.


# Releases
## uptide@0.18.0

### Minor Changes

- [#261](#261)
[`93f029e`](93f029e)
Thanks [@lucasbraum](https://github.com/lucasbraum)! - Sentry 10 → 11:
report every `Sentry.init` whose data collection widens because
`dataCollection` is unset, citing the migration guide, and give the
agent the guide's exact v10 baseline. After a fix, a site counts as kept
only when every category is at least that restrictive, so a partial edit
such as a cookie deny list stays open in the PR instead of being
reported as kept. The agent now keeps privacy settings at their most
restrictive value, or leaves them as a decision, when no cited edit
exists.

- [#253](#253)
[`e48373d`](e48373d)
Thanks [@lucasbraum](https://github.com/lucasbraum)! - Find Stripe's
clover billing-mode default change at subscription creation sites,
across the six affected APIs. Respect request API-version pins, preserve
classic billing at documented parameter paths, and report unresolved
parameters or semantics as decisions. Verify quoted sources against
full-page hashes at pack-build time without vendoring Stripe pages.
Score the dms-saas silent billing change in UpgradeBench.

### Patch Changes

- [#260](#260)
[`8ea1a87`](8ea1a87)
Thanks [@lucasbraum](https://github.com/lucasbraum)! - A build that
fails the same way before and after the upgrade (same command, status
and output; only durations and clock times may differ) no longer fails
verification. Builds are now compared to the baseline the same way tests
are. The PR says `⚠️ build: failing at baseline, not checked`, and the
verdict reads `no new errors`, not `verification passed`. A build that
passed at baseline and fails after the upgrade still fails, and so does
one that fails differently. Without a pack, a run like this still does
not publish, because its build cannot verify the bump.

- [#259](#259)
[`0db73c8`](0db73c8)
Thanks [@lucasbraum](https://github.com/lucasbraum)! - Let the agent add
a property the upgraded types made required to a test fixture: only to
the object the compiler's TS2739, TS2740 or TS2741 error points at, and
only with a literal value. Every other test input change is still
rejected. Each such property is listed under "Tests changed: review" in
the PR body and in report.json (file, line, property and the value the
agent chose). Name a test that asserts a dependency default the upgrade
changed (Sentry's `dataCollection` and `attachStacktrace`) under
"Decisions for you", instead of leaving it to fail unexplained.

- [#251](#251)
[`773dbbe`](773dbbe)
Thanks [@lucasbraum](https://github.com/lucasbraum)! - Calibrate the SDK
pre-run cost estimate from recorded migrations, accounting for shared
rule knowledge instead of charging a full session allowance per site.
Keep the configured spending limit and per-call budget enforcement
unchanged.

- [#258](#258)
[`3a4898f`](3a4898f)
Thanks [@lucasbraum](https://github.com/lucasbraum)! - Discover
independent nested projects when fixing a dependency absent from the
root project. Run installation, migration and verification in the
selected project's directory, preserving its lockfile and package
manager. When several projects declare the dependency, name their
manifests and require an explicit directory.

- [#257](#257)
[`7b9ed7f`](7b9ed7f)
Thanks [@lucasbraum](https://github.com/lucasbraum)! - Run pinned npm
through Corepack when local npm differs. When the pin is unavailable,
allow local npm with a compatible v3 lockfile, record the fallback in
the report, and reject frozen installs that rewrite the lockfile.

Co-authored-by: uptide-release[bot] <338861287+uptide-release[bot]@users.noreply.github.com>
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.

1 participant