Skip to content

refactor: Introduce centralized snapshot-based test project infrastructure with new CLI commands and improved storage structure#789

Merged
kingston merged 13 commits into
mainfrom
kingston/eng-1040-testing-infra-snapshot-diff-storage-enhancement
Mar 2, 2026
Merged

refactor: Introduce centralized snapshot-based test project infrastructure with new CLI commands and improved storage structure#789
kingston merged 13 commits into
mainfrom
kingston/eng-1040-testing-infra-snapshot-diff-storage-enhancement

Conversation

@kingston

@kingston kingston commented Mar 2, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • New CLI: test-project (init, generate, save, run-env) and test (gen, web) to create, generate, save, run, and execute snapshot-based test projects.
    • Generation manifest and staleness checks to track generated test outputs.
  • Improvements

    • Standardized storage layout for test projects and generated outputs (test-projects/… and gitignored generated-tests/…).
    • Enhanced snapshot apply/save behavior and multi-app snapshot support.
  • Chores

    • Updated example ignore rules, workflows, and test helpers; added unit tests for snapshot/generation flows.

@changeset-bot

changeset-bot Bot commented Mar 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 08be363

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@baseplate-dev/project-builder-dev Patch
@baseplate-dev/project-builder-server Patch
@baseplate-dev/project-builder-test Patch
@baseplate-dev/create-project Patch
@baseplate-dev/project-builder-cli Patch
@baseplate-dev/project-builder-common Patch
@baseplate-dev/project-builder-web Patch
@baseplate-dev/code-morph Patch
@baseplate-dev/core-generators Patch
@baseplate-dev/fastify-generators Patch
@baseplate-dev/project-builder-lib Patch
@baseplate-dev/react-generators Patch
@baseplate-dev/sync Patch
@baseplate-dev/tools Patch
@baseplate-dev/ui-components Patch
@baseplate-dev/utils Patch
@baseplate-dev/plugin-auth Patch
@baseplate-dev/plugin-email Patch
@baseplate-dev/plugin-queue Patch
@baseplate-dev/plugin-rate-limit Patch
@baseplate-dev/plugin-storage Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Mar 2, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@kingston has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 35 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 7fd5de0 and 08be363.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • packages/project-builder-dev/src/commands/snapshot.ts
  • packages/project-builder-dev/src/e2e-runner/discover-tests.ts
  • packages/project-builder-dev/src/e2e-runner/generation-manifest.ts
  • packages/project-builder-server/src/actions/snapshot/snapshot-show.action.ts
  • packages/project-builder-server/src/diff/snapshot/apply-diff-to-generator-output.ts
  • packages/project-builder-server/src/diff/snapshot/snapshot-manifest.ts
📝 Walkthrough

Walkthrough

Adds centralized snapshot-based test project infrastructure: new dev CLI commands (test-project and test), e2e-runner with discovery/runner, generation manifest and staleness checks, snapshot layout moved under baseplate/snapshots/, renames snapshotDirectory→baseplateDirectory, and new server actions for test-project lifecycle.

Changes

Cohort / File(s) Summary
CLI — new commands
packages/project-builder-dev/src/commands/test-project.ts, packages/project-builder-dev/src/commands/test.ts, packages/project-builder-dev/src/index.ts
Adds top-level test-project (init, generate, save, run-env) and test (gen) command groups and wires them into dev CLI.
E2E runner & API surface
packages/project-builder-dev/src/e2e-runner/*, packages/project-builder-dev/src/e2e-runner/index.ts, packages/project-builder-dev/package.json
New e2e-runner module: test discovery, runner, environment helpers, generation-manifest; re-exports public types and adds runtime deps.
Server actions — test-project
packages/project-builder-server/src/actions/test-project/*, packages/project-builder-server/src/actions/index.ts, packages/project-builder-server/src/actions/registry.ts
Adds testProjectInit/Generate/Save actions, exports them, and registers actions in the server registry.
Snapshot management refactor
packages/project-builder-server/src/diff/snapshot/*, packages/project-builder-server/src/diff/snapshot/*.unit.test.ts, packages/project-builder-server/src/diff/snapshot/snapshot-types.ts, packages/project-builder-server/src/diff/snapshot/snapshot-utils.ts
Refactors snapshots to use objects for added files (path + optional contentFile), moves snapshot layout to baseplate/snapshots/, supports storing added file contents, updates manifest shape, and adds manifest utilities and unit tests.
Apply snapshot during generation
packages/project-builder-server/src/sync/generate-for-directory.ts, packages/project-builder-server/src/diff/snapshot/apply-diff-to-generator-output.ts
Integrates applySnapshotToGeneratorOutput into generation flow, applies diffs with fuzzFactor, injects stored added contents, and adjusts overwrite/apply-diff logic.
API & option rename: snapshotDirectory → baseplateDirectory
packages/project-builder-server/src/sync/*, packages/project-builder-server/src/actions/snapshot/*, packages/project-builder-server/src/actions/utils/project-discovery.ts, packages/project-builder-server/src/project-definition/*.ts
Renames and propagates option from snapshotDirectory to baseplateDirectory across loading, discovery, sync, and snapshot management; adds resolveBaseplateDir helper and baseplateDirectory overrides.
Test-project generation manifest & staleness
packages/project-builder-dev/src/e2e-runner/generation-manifest.ts, packages/project-builder-dev/src/commands/test-project.ts
Adds generation manifest writer/checker, staleness detection, and asserts to prevent overwriting stale generated-tests output.
Remove old project-builder-test CLI
packages/project-builder-test/src/**/* (index, commands, runner, utils), packages/project-builder-test/package.json, packages/project-builder-test/turbo.json
Deletes legacy CLI entrypoint, test runner commands (test, generate, run, serve), and supporting utils; updates package scripts to new test-project tooling.
Examples & snapshots
examples/*/baseplate/snapshots/*/manifest.json, examples/*/.baseplateignore, packages/project-builder-test/test-projects/*/snapshots/*
Adds/updates example snapshot manifests and diffs; expands .baseplateignore entries (pnpm-lock.yaml, env patterns) and adds generated test snapshots.
Repo ignores & tooling
.gitignore, packages/project-builder-test/.gitignore, .coderabbit.yaml, packages/project-builder-test/eslint.config.js, .github/workflows/test-e2e.yml
Adds generated-tests/ to gitignore, excludes generated-tests in ESLint and CodeRabbit, updates Playwright workflow to run test generation after E2E.
Tests & helpers
packages/project-builder-server/src/diff/snapshot/*.unit.test.ts, packages/project-builder-server/src/tests/helpers/generator-output.test-helper.ts, packages/sync/src/utils/ignore-patterns.unit.test.ts
New and updated unit tests covering snapshot save/apply, manifest utils, snapshot-utils, and ignore-pattern behavior; adds generator output test helper.
Minor runtime & logging changes
packages/core-generators/.../prettier.generator.ts, packages/project-builder-dev/src/e2e-runner/*/utils/*.ts, packages/sync/src/output/post-write-commands/run-commands.ts
Improves Prettier error logging; replaces some internal logger usage with console.* in e2e-runner utilities; small output formatting tweak.
Exports & package metadata
packages/project-builder-dev/package.json, packages/project-builder-server/src/index.ts, packages/project-builder-server/src/compiler/index.ts, knip.config.js, package.json
Adds package exports (e2e-runner), re-exports project-definition and new types, adjusts knip entries, and updates root/test package scripts for new commands.

Sequence Diagram(s)

sequenceDiagram
    participant User as CLI User
    participant DevCLI as Dev CLI (test-project)
    participant ServerAction as Server Action
    participant Generator as Generator
    participant Snapshot as Generation Manifest / Snapshot
    participant Env as Environment Helper

    User->>DevCLI: generate <test-name>
    DevCLI->>ServerAction: invoke testProjectGenerateAction
    ServerAction->>Generator: generate project output
    Generator-->>ServerAction: generated output
    ServerAction->>Snapshot: checkStaleness(outputDir)
    alt not stale
        ServerAction->>Snapshot: writeGenerationManifest(outputDir)
        ServerAction-->>User: generation complete
    else stale
        ServerAction-->>User: error (stale) -> abort
    end

    User->>DevCLI: run-env <test-name>
    DevCLI->>ServerAction: discover test definition
    ServerAction->>Env: createEnvironmentHelpers + setupEnvironment
    Env-->>User: environment ready (wait for signal)
    User-->>Env: shutdown signal
    Env->>Env: teardown
    Env-->>User: environment stopped
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the changeset: introducing centralized snapshot-based test project infrastructure with new CLI commands and improved storage structure. It is specific, clear, and directly summarizes the primary changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kingston/eng-1040-testing-infra-snapshot-diff-storage-enhancement

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/project-builder-dev/src/commands/sync.ts (1)

25-27: ⚠️ Potential issue | 🟡 Minor

The --snapshot option help text is misleading about what directory it expects.

The CLI option describes "Apply diffs from snapshot directory", but it actually passes the value to baseplateDirectory, which is documented as "Custom baseplate directory. Defaults to path.join(directory, 'baseplate')". These are different—the snapshot directory is derived from the baseplate directory, not the other way around.

Update the help text to clarify: "Custom baseplate directory (defaults to <project>/baseplate)" or rename the option to --baseplate for consistency with the parameter name.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-dev/src/commands/sync.ts` around lines 25 - 27, The
help text for the CLI option '--snapshot' is misleading because its value is
passed into the baseplateDirectory parameter (used as "Custom baseplate
directory" and defaulted to path.join(directory, 'baseplate')); either change
the option name to '--baseplate <directory>' to match the parameter or update
the help text to say "Custom baseplate directory (defaults to
<project>/baseplate)" so it accurately documents baseplateDirectory; locate the
option definition near the sync command where '--snapshot <directory>' is
declared and update the flag name or the description to match the
baseplateDirectory semantics.
packages/project-builder-server/src/diff/snapshot/snapshot-utils.unit.test.ts (1)

188-204: ⚠️ Potential issue | 🟠 Major

Don’t skip underscore round-trip cases; this hides a real path-mapping bug.

The skip branch accepts lossy conversion for filenames containing _, which can mis-map snapshot diffs to incorrect paths. This should be asserted as a failing case and fixed in packages/project-builder-server/src/diff/snapshot/snapshot-utils.ts rather than bypassed in tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/diff/snapshot/snapshot-utils.unit.test.ts`
around lines 188 - 204, The test is skipping filenames with underscores which
masks a real lossiness in pathToSafeDiffFilename / safeDiffFilenameToPath;
remove the test skip and instead fix the conversion logic in snapshot-utils.ts
so underscores round-trip. Update pathToSafeDiffFilename to escape or encode
underscore characters in a reversible way (and escape whatever separator you
use), and implement the complementary decoding in safeDiffFilenameToPath so
safeDiffFilenameToPath(pathToSafeDiffFilename(x)) === x for inputs containing
'_'; then re-enable the underscore cases in the unit test to assert the
corrected behavior.
packages/project-builder-server/src/actions/snapshot/snapshot-save.action.ts (1)

64-66: ⚠️ Potential issue | 🟡 Minor

Warning message should reflect all-app mode.

When app is omitted, the current warning still says “this app”, which is misleading before an overwrite operation.

✏️ Suggested fix
-        logger.warn(
-          '⚠️  This will overwrite any existing snapshot for this app.',
-        );
+        logger.warn(
+          `⚠️  This will overwrite any existing snapshot for ${app ? `app '${app}'` : 'all apps'}.`,
+        );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-server/src/actions/snapshot/snapshot-save.action.ts`
around lines 64 - 66, The warning currently logged via logger.warn('⚠️  This
will overwrite any existing snapshot for this app.') is misleading when the
caller omits the app parameter; update the code in snapshot-save.action.ts (the
place where logger.warn is called) to conditionally log a message based on the
presence of the app variable: if app is provided log "⚠️ This will overwrite any
existing snapshot for this app." otherwise log "⚠️ This will overwrite any
existing snapshots for all apps." Ensure you reference the existing logger.warn
call and the local parameter/variable named app when implementing this
conditional message.
🧹 Nitpick comments (5)
packages/project-builder-server/src/diff/diff-project.ts (1)

149-153: Prefer shared baseplate-directory resolution over inline 'baseplate' join.

Line 149 hardcodes the baseplate root. If baseplate path resolution is centralized in other actions, this can drift and cause snapshot lookup mismatches later. Reuse the same resolver/helper used elsewhere for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-server/src/diff/diff-project.ts` around lines 149 -
153, Replace the hardcoded baseplateDirectory = path.join(project.directory,
'baseplate') with a call to the project’s centralized baseplate-directory
resolver (the same helper used elsewhere) so snapshot resolution uses the
canonical path; call that shared helper with project.directory to produce
baseplateDirectory and then pass it into
resolveSnapshotDirectory(projectBaseplateDir, app.name) (keep references to
baseplateDirectory, project.directory, resolveSnapshotDirectory, and app.name to
locate the change).
packages/project-builder-dev/src/commands/snapshot.ts (1)

89-117: Keep CLI error handling/logging consistent across subcommands.

save and show dropped the local try/catch pattern used in add/remove. Wrap these actions similarly so failures are logged with command-specific context before rethrowing.

Proposed consistency patch
-    .action(async (project: string, app: string | undefined) => {
+    .action(async (project: string, app: string | undefined) => {
+      try {
       // Confirm with user before overwriting existing snapshot
       const target = app ?? 'all apps';
       console.warn(
         `⚠️  This will overwrite any existing snapshot for ${target}.`,
       );
       console.info(
         'Use granular commands (snapshot add/remove) for safer updates.',
       );
       const proceed: boolean = await confirm({
         message: 'Are you sure you want to overwrite the existing snapshot?',
         default: false,
       });
       if (!proceed) {
         logger.info('Aborted snapshot save.');
         return;
       }

       const context = await createServiceActionContext();

       await invokeServiceActionAsCli(
         snapshotSaveAction,
         {
           project,
           app,
         },
         context,
       );
+      } catch (error) {
+        logger.error('Failed to save snapshot:', error);
+        throw error;
+      }
     });
...
-    .action(async (project: string, app: string) => {
-      const context = await createServiceActionContext();
+    .action(async (project: string, app: string) => {
+      try {
+        const context = await createServiceActionContext();

       await invokeServiceActionAsCli(
         snapshotShowAction,
         {
           project,
           app,
         },
         context,
       );
+      } catch (error) {
+        logger.error('Failed to show snapshot:', error);
+        throw error;
+      }
     });

Also applies to: 123-134

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-dev/src/commands/snapshot.ts` around lines 89 - 117,
The snapshot CLI handlers for "save" (where snapshotSaveAction and
invokeServiceActionAsCli are called) and "show" need the same try/catch error
handling used by "add"/"remove": wrap the async action body (after confirm and
createServiceActionContext) in try { ... await
invokeServiceActionAsCli(snapshotSaveAction, { project, app }, context) } catch
(err) { logger.error(`Failed to save snapshot for project=${project} app=${app
?? 'all'}: ${err}`); throw err } — do the analogous try/catch/log in the "show"
handler (referencing snapshotShowAction or the show handler) so failures are
logged with command-specific context before rethrowing.
packages/project-builder-server/src/actions/test-project/test-project-save.action.ts (1)

14-17: Avoid returning/compiling unused apps in loadTestProjectContext.

compilePackages(...) is currently not consumed by this file’s save flow, so this can be trimmed (or explicitly documented as intentional validation) to reduce unnecessary work.

Also applies to: 55-57, 81-87

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/actions/test-project/test-project-save.action.ts`
around lines 14 - 17, The TestProjectContext currently includes and returns an
unused apps array and calls compilePackages in loadTestProjectContext; remove
the unused work by deleting the apps field from the TestProjectContext interface
and stop invoking/returning compilePackages in loadTestProjectContext (or, if
compilation is intentionally a validation step, add an explicit comment and keep
only its side-effects), updating all references to apps in this file and
removing the compilePackages(...) call sites so the save flow no longer performs
unnecessary package compilation.
packages/project-builder-dev/src/e2e-runner/runner.ts (1)

37-40: Avoid repeated plugin/version discovery inside each test run.

Resolve plugins and cliVersion once in runTests and pass them into runTest to reduce repeated I/O and package lookup overhead.

♻️ Suggested refactor
-async function runTest(
+async function runTest(
   test: ProjectBuilderTest,
   testProjectsDir: string,
   outputDir: string,
+  shared: { plugins: Awaited<ReturnType<typeof discoverPlugins>>; cliVersion: string },
 ): Promise<void> {
@@
-  const plugins = await discoverPlugins(process.cwd(), console);
-  const cliVersion = (await getPackageVersion(import.meta.dirname)) ?? '0.0.0';
+  const { plugins, cliVersion } = shared;
@@
 export async function runTests(options: RunTestsOptions): Promise<void> {
@@
+  const plugins = await discoverPlugins(process.cwd(), console);
+  const cliVersion = (await getPackageVersion(import.meta.dirname)) ?? '0.0.0';
@@
-    await runTest(test.content, testProjectsDir, outputDir);
+    await runTest(test.content, testProjectsDir, outputDir, {
+      plugins,
+      cliVersion,
+    });

Also applies to: 86-94

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-dev/src/e2e-runner/runner.ts` around lines 37 - 40,
Resolve plugins and cliVersion once in runTests by calling
discoverPlugins(process.cwd(), console) and
getPackageVersion(import.meta.dirname) at the start of runTests, store results
in local variables (e.g., plugins and cliVersion), and pass them as parameters
into runTest (and any calls to generateTestProject) instead of calling
discoverPlugins/getPackageVersion inside runTest; update runTest signature and
all internal uses to accept and use the passed plugins and cliVersion values to
avoid repeated I/O and package lookups.
packages/project-builder-dev/src/commands/test-project.ts (1)

197-198: Scope discovery to the requested test in run-env.

run-env <test-name> currently imports every *.gen.ts before selecting one, so unrelated broken test defs can block this command.

♻️ Suggested fix
-      const tests = await discoverTests(testDefinitionsDir);
+      const tests = await discoverTests(testDefinitionsDir, testName);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-dev/src/commands/test-project.ts` around lines 197 -
198, The command currently calls discoverTests(testDefinitionsDir) which imports
every *.gen.ts before filtering, so modify discovery to avoid importing
unrelated files: update discoverTests (or add a new function, e.g.,
discoverTestByName) to accept an optional filter parameter (testName) and
implement a two-step scan—first read filenames/AST or parse each file's exported
metadata without executing the module, or map filenames to projectDirectory by
reading the file contents to find the projectDirectory string—then only import
the matching module(s). In test-project.ts replace the unconditional await
discoverTests(testDefinitionsDir) + tests.find(...) with a call that passes the
requested testName (from run-env) to the new/updated discovery function so only
the target *.gen.ts is loaded and executed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/project-builder-dev/src/commands/snapshot.ts`:
- Around line 85-117: The save snapshot command currently always prompts before
overwriting; add a boolean CLI flag option('--force', 'Skip confirmation
prompt', false) to the command chain and update the .action signature to accept
the options parameter (e.g., .action(async (project: string, app: string |
undefined, options: { force?: boolean }) => { ... })). Inside the action, skip
calling confirm(...) when options.force is true (preserve the existing confirm
flow when false), and keep the existing calls to createServiceActionContext(),
invokeServiceActionAsCli(snapshotSaveAction, { project, app }, context), and
logger.info('Aborted snapshot save.') unchanged. Ensure the unique symbols
referenced are options.force, confirm, snapshotSaveAction,
invokeServiceActionAsCli, createServiceActionContext, and logger.

In `@packages/project-builder-dev/src/e2e-runner/discover-tests.ts`:
- Line 16: The glob passed to globby when building testFiles won't match files
in subdirectories; update the globby call in discover-tests.ts (the testFiles =
await globby(...) usage) to use a recursive pattern '**/*.gen.ts' instead of
'**.gen.ts' so nested files like src/tests/simple.gen.ts are discovered; modify
only the pattern string passed to globby and keep existing options intact.

In `@packages/project-builder-dev/src/e2e-runner/generation-manifest.ts`:
- Around line 106-129: The staleness check only looks for new or changed files
and ignores deletions; update the logic (in generation-manifest.ts around the
currentFiles mapping where collectFiles, hashFile, manifest.files and
FILE_HASH_CONCURRENCY are used) to compute deletedFiles by comparing
Object.keys(manifest.files) against the currentFiles list (or a Set of
currentFiles), add any missing manifest entries to deletedFiles, include
deletedFiles in the returned object, and ensure isStale becomes true if
deletedFiles.length > 0.
- Around line 98-104: The catch block that wraps readFile(manifestPath, 'utf-8')
and JSON.parse(...) is masking all errors; change it so only a missing file
(e.g., ENOENT) is treated as "not stale" and returns { isStale: false,
modifiedFiles: [], newFiles: [] }, while other errors (JSON.parse failures,
permission errors, etc.) are rethrown or bubbled up so callers can fail safely;
update the try/catch around readFile/JSON.parse in generation-manifest.ts
(referencing manifestPath, GenerationManifest, readFile, and the early return
shape) to inspect the error code and only swallow file-not-found, otherwise
throw the error.

In
`@packages/project-builder-server/src/actions/snapshot/snapshot-show.action.ts`:
- Line 1: The action hardcodes "project/baseplate" when building snapshot
filesystem paths; update SnapshotShowAction (in snapshot-show.action.ts) to use
the project's baseplateDirectory instead of the literal string: replace
occurrences that build paths like "project/baseplate" in functions/methods such
as snapshotShowAction and any local resolveSnapshotPath helper to compute paths
via path.join(project.path, project.baseplateDirectory, ...) or
path.resolve(project.root, project.baseplateDirectory, ...) (use the existing
project or ctx.project object and its baseplateDirectory property), and ensure
all four occurrences referenced (around the earlier path uses and the
check/report logic) are updated so missing-snapshot checks use the configured
baseplateDirectory.

In `@packages/project-builder-server/src/actions/sync/sync-project.action.ts`:
- Around line 20-25: The schema rename from snapshotDirectory to
baseplateDirectory will break callers; update the input handling for the sync
project action so both keys are accepted during migration: keep the existing zod
field baseplateDirectory but also accept the deprecated snapshotDirectory
(alias) and, if snapshotDirectory is provided, map/copy its value into
baseplateDirectory before any fallback resolution logic (the code that currently
reads baseplateDirectory and applies the fallback on missing value). Reference
the schema field baseplateDirectory and the old key snapshotDirectory and ensure
the resolution code that computes the final directory (the early fallback logic
currently triggered around the resolution step) uses the normalized
baseplateDirectory value.

In
`@packages/project-builder-server/src/diff/snapshot/apply-diff-to-generator-output.ts`:
- Around line 52-57: The check after reading the content file treats empty
string as missing; change the validation so an explicit file-not-found is
detected via the catch handler (handleFileNotFoundError) while allowing '' as a
valid value—i.e., keep the await readFile(contentFilePath,
'utf-8').catch(handleFileNotFoundError) but replace the truthy check on contents
with a test that distinguishes read failure (the error returned by
handleFileNotFoundError or undefined/null) from an empty string, and throw `new
Error(\`Content file not found: ${contentFilePath}\`)` only when the read
actually failed; locate this logic around the readFile call and the `contents`
variable in apply-diff-to-generator-output.ts.

In
`@packages/project-builder-server/src/diff/snapshot/create-snapshot-for-project.ts`:
- Around line 151-166: The compiled list from compilePackages(...) can include
non-app packages, so ensure appsToProcess only contains application packages:
after const apps = compilePackages(projectJson, context) filter that array
(e.g., const appsOnly = apps.filter(isApp)) where isApp checks the PackageEntry
marker for an application (package type/manifest flag used in your codebase),
then use appsOnly for the appName lookup and as the default list (replace uses
of apps with appsOnly when computing appsToProcess and validating appName). Also
add a clear error when appName is provided but matches a non-app package by
searching appsOnly rather than apps.

In `@packages/project-builder-server/src/diff/snapshot/snapshot-manifest.ts`:
- Around line 95-106: The current early-return when manifest.files.added
contains an entry with the same path prevents enriching an existing entry with
contentFile; update addAddedFile so it does not return immediately but instead
maps manifest.files.added and replaces the matching entry with one that includes
contentFile when provided (e.g., in the block that currently checks
manifest.files.added.some((entry) => entry.path === filePath), change behavior
to transform the existing entry to { path: filePath, ...(contentFile && {
contentFile }) } rather than returning manifest), ensuring new entries are still
appended when no match is found and preserving other manifest fields.

---

Outside diff comments:
In `@packages/project-builder-dev/src/commands/sync.ts`:
- Around line 25-27: The help text for the CLI option '--snapshot' is misleading
because its value is passed into the baseplateDirectory parameter (used as
"Custom baseplate directory" and defaulted to path.join(directory,
'baseplate')); either change the option name to '--baseplate <directory>' to
match the parameter or update the help text to say "Custom baseplate directory
(defaults to <project>/baseplate)" so it accurately documents
baseplateDirectory; locate the option definition near the sync command where
'--snapshot <directory>' is declared and update the flag name or the description
to match the baseplateDirectory semantics.

In
`@packages/project-builder-server/src/actions/snapshot/snapshot-save.action.ts`:
- Around line 64-66: The warning currently logged via logger.warn('⚠️  This will
overwrite any existing snapshot for this app.') is misleading when the caller
omits the app parameter; update the code in snapshot-save.action.ts (the place
where logger.warn is called) to conditionally log a message based on the
presence of the app variable: if app is provided log "⚠️ This will overwrite any
existing snapshot for this app." otherwise log "⚠️ This will overwrite any
existing snapshots for all apps." Ensure you reference the existing logger.warn
call and the local parameter/variable named app when implementing this
conditional message.

In
`@packages/project-builder-server/src/diff/snapshot/snapshot-utils.unit.test.ts`:
- Around line 188-204: The test is skipping filenames with underscores which
masks a real lossiness in pathToSafeDiffFilename / safeDiffFilenameToPath;
remove the test skip and instead fix the conversion logic in snapshot-utils.ts
so underscores round-trip. Update pathToSafeDiffFilename to escape or encode
underscore characters in a reversible way (and escape whatever separator you
use), and implement the complementary decoding in safeDiffFilenameToPath so
safeDiffFilenameToPath(pathToSafeDiffFilename(x)) === x for inputs containing
'_'; then re-enable the underscore cases in the unit test to assert the
corrected behavior.

---

Nitpick comments:
In `@packages/project-builder-dev/src/commands/snapshot.ts`:
- Around line 89-117: The snapshot CLI handlers for "save" (where
snapshotSaveAction and invokeServiceActionAsCli are called) and "show" need the
same try/catch error handling used by "add"/"remove": wrap the async action body
(after confirm and createServiceActionContext) in try { ... await
invokeServiceActionAsCli(snapshotSaveAction, { project, app }, context) } catch
(err) { logger.error(`Failed to save snapshot for project=${project} app=${app
?? 'all'}: ${err}`); throw err } — do the analogous try/catch/log in the "show"
handler (referencing snapshotShowAction or the show handler) so failures are
logged with command-specific context before rethrowing.

In `@packages/project-builder-dev/src/commands/test-project.ts`:
- Around line 197-198: The command currently calls
discoverTests(testDefinitionsDir) which imports every *.gen.ts before filtering,
so modify discovery to avoid importing unrelated files: update discoverTests (or
add a new function, e.g., discoverTestByName) to accept an optional filter
parameter (testName) and implement a two-step scan—first read filenames/AST or
parse each file's exported metadata without executing the module, or map
filenames to projectDirectory by reading the file contents to find the
projectDirectory string—then only import the matching module(s). In
test-project.ts replace the unconditional await
discoverTests(testDefinitionsDir) + tests.find(...) with a call that passes the
requested testName (from run-env) to the new/updated discovery function so only
the target *.gen.ts is loaded and executed.

In `@packages/project-builder-dev/src/e2e-runner/runner.ts`:
- Around line 37-40: Resolve plugins and cliVersion once in runTests by calling
discoverPlugins(process.cwd(), console) and
getPackageVersion(import.meta.dirname) at the start of runTests, store results
in local variables (e.g., plugins and cliVersion), and pass them as parameters
into runTest (and any calls to generateTestProject) instead of calling
discoverPlugins/getPackageVersion inside runTest; update runTest signature and
all internal uses to accept and use the passed plugins and cliVersion values to
avoid repeated I/O and package lookups.

In
`@packages/project-builder-server/src/actions/test-project/test-project-save.action.ts`:
- Around line 14-17: The TestProjectContext currently includes and returns an
unused apps array and calls compilePackages in loadTestProjectContext; remove
the unused work by deleting the apps field from the TestProjectContext interface
and stop invoking/returning compilePackages in loadTestProjectContext (or, if
compilation is intentionally a validation step, add an explicit comment and keep
only its side-effects), updating all references to apps in this file and
removing the compilePackages(...) call sites so the save flow no longer performs
unnecessary package compilation.

In `@packages/project-builder-server/src/diff/diff-project.ts`:
- Around line 149-153: Replace the hardcoded baseplateDirectory =
path.join(project.directory, 'baseplate') with a call to the project’s
centralized baseplate-directory resolver (the same helper used elsewhere) so
snapshot resolution uses the canonical path; call that shared helper with
project.directory to produce baseplateDirectory and then pass it into
resolveSnapshotDirectory(projectBaseplateDir, app.name) (keep references to
baseplateDirectory, project.directory, resolveSnapshotDirectory, and app.name to
locate the change).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3b3be2b and ff24d52.

⛔ Files ignored due to path filters (197)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • tests/simple/.gitignore is excluded by !tests/**
  • tests/simple/.npmrc is excluded by !tests/**
  • tests/simple/.prettierignore is excluded by !tests/**
  • tests/simple/.prettierrc is excluded by !tests/**
  • tests/simple/README.md is excluded by !tests/**
  • tests/simple/apps/backend/.env.example is excluded by !tests/**
  • tests/simple/apps/backend/.gitignore is excluded by !tests/**
  • tests/simple/apps/backend/.prettierignore is excluded by !tests/**
  • tests/simple/apps/backend/.prettierrc is excluded by !tests/**
  • tests/simple/apps/backend/README.md is excluded by !tests/**
  • tests/simple/apps/backend/baseplate/file-id-map.json is excluded by !tests/**
  • tests/simple/apps/backend/baseplate/generated/.env.example is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/.gitignore is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/.prettierignore is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/.prettierrc is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/README.md is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/eslint.config.js is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/package.json is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/prisma.config.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/prisma/schema.prisma is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/index.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/instrument.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/blog/index.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/blog/schema/blog-post.object-type.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/blog/schema/blog-post.queries.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/graphql/index.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/graphql/scalars/date-time.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/graphql/scalars/date.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/graphql/scalars/json-object.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/graphql/scalars/json.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/graphql/scalars/uuid.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/modules/index.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/error-handler.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graceful-shutdown.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/FieldWithInputPayloadPlugin/global-types.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/FieldWithInputPayloadPlugin/index.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/FieldWithInputPayloadPlugin/schema-builder.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/FieldWithInputPayloadPlugin/types.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/builder.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/index.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/strip-query-mutation-plugin.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/use-graph-logger.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/graphql/use-sentry.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/health-check.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/plugins/request-context.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/prisma/seed.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/server.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/services/config.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/services/error-logger.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/services/logger.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/services/prisma.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/services/sentry.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/tests/helpers/db.test-helper.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/tests/helpers/logger.test-helper.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/tests/helpers/prisma.test-helper.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/tests/helpers/service-context.test-helper.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/tests/scripts/global-setup-prisma.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/app-modules.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/authorizers.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/data-operations/define-operations.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/data-operations/field-definitions.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/data-operations/prisma-types.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/data-operations/prisma-utils.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/data-operations/relation-helpers.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/data-operations/types.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/http-errors.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/request-service-context.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/service-context.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/string.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/src/utils/zod.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/tsconfig.json is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/baseplate/generated/vitest.config.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/backend/eslint.config.js is excluded by !tests/**
  • tests/simple/apps/backend/package.json is excluded by !tests/**
  • tests/simple/apps/backend/prisma.config.ts is excluded by !tests/**
  • tests/simple/apps/backend/prisma/migrations/migration_lock.toml is excluded by !tests/**
  • tests/simple/apps/backend/prisma/schema.prisma is excluded by !tests/**
  • tests/simple/apps/backend/schema.graphql is excluded by !tests/**
  • tests/simple/apps/backend/src/index.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/instrument.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/blog/index.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/blog/schema/blog-post.object-type.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/blog/schema/blog-post.queries.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/graphql/index.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/graphql/scalars/date-time.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/graphql/scalars/date.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/graphql/scalars/json-object.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/graphql/scalars/json.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/graphql/scalars/uuid.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/modules/index.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/error-handler.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graceful-shutdown.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/FieldWithInputPayloadPlugin/global-types.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/FieldWithInputPayloadPlugin/index.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/FieldWithInputPayloadPlugin/schema-builder.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/FieldWithInputPayloadPlugin/types.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/builder.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/index.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/strip-query-mutation-plugin.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/use-graph-logger.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/graphql/use-sentry.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/health-check.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/plugins/request-context.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/prisma/seed.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/server.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/services/config.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/services/error-logger.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/services/logger.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/services/prisma.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/services/sentry.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/tests/helpers/db.test-helper.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/tests/helpers/logger.test-helper.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/tests/helpers/prisma.test-helper.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/tests/helpers/service-context.test-helper.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/tests/scripts/global-setup-prisma.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/app-modules.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/authorizers.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/data-operations/define-operations.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/data-operations/field-definitions.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/data-operations/prisma-types.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/data-operations/prisma-utils.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/data-operations/relation-helpers.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/data-operations/types.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/http-errors.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/request-service-context.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/service-context.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/string.ts is excluded by !tests/**
  • tests/simple/apps/backend/src/utils/zod.ts is excluded by !tests/**
  • tests/simple/apps/backend/tsconfig.json is excluded by !tests/**
  • tests/simple/apps/backend/vitest.config.ts is excluded by !tests/**
  • tests/simple/apps/e2e/.gitignore is excluded by !tests/**
  • tests/simple/apps/e2e/.prettierignore is excluded by !tests/**
  • tests/simple/apps/e2e/.prettierrc is excluded by !tests/**
  • tests/simple/apps/e2e/package.json is excluded by !tests/**
  • tests/simple/apps/e2e/playwright.config.ts is excluded by !tests/**
  • tests/simple/apps/e2e/tests/main.spec.ts is excluded by !tests/**
  • tests/simple/apps/e2e/tsconfig.json is excluded by !tests/**
  • tests/simple/apps/web/.env.development is excluded by !tests/**
  • tests/simple/apps/web/.gitignore is excluded by !tests/**
  • tests/simple/apps/web/.prettierignore is excluded by !tests/**
  • tests/simple/apps/web/.prettierrc is excluded by !tests/**
  • tests/simple/apps/web/README.md is excluded by !tests/**
  • tests/simple/apps/web/baseplate/file-id-map.json is excluded by !tests/**
  • tests/simple/apps/web/baseplate/generated/.env.development is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/.gitignore is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/.prettierignore is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/.prettierrc is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/README.md is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/eslint.config.js is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/graphql.config.ts is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/index.html is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/package.json is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/public/favicon.ico is excluded by !**/*.ico, !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/app/app-apollo-provider.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/app/app.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/app/router.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/alert.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/async-boundary.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/badge.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/breadcrumb.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/button.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/calendar.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/card.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/checkbox-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/checkbox.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/circular-progress.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/combobox-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/combobox.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/command.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/confirm-dialog.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/date-picker-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/date-time-picker-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/dialog.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/dropdown.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/empty-display.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/error-boundary.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/error-display.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/errorable-loader.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/form-item.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/input-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/input.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/label.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/loader.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/multi-combobox-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/multi-combobox.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/navigation-menu.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/not-found-card.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/popover.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/scroll-area.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/select-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/select.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/separator.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/sheet.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/sidebar.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/skeleton.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
  • tests/simple/apps/web/baseplate/generated/src/components/ui/switch-field.tsx is excluded by !**/generated/**, !tests/**, !**/generated/**
📒 Files selected for processing (103)
  • .changeset/test-case-infrastructure.md
  • .coderabbit.yaml
  • .github/workflows/test-e2e.yml
  • .gitignore
  • examples/blog-with-auth/.baseplateignore
  • examples/blog-with-auth/apps/backend/.baseplateignore
  • examples/blog-with-auth/baseplate/snapshots/admin/diffs/package.json.diff
  • examples/blog-with-auth/baseplate/snapshots/admin/manifest.json
  • examples/blog-with-auth/baseplate/snapshots/backend/manifest.json
  • examples/blog-with-auth/baseplate/snapshots/root/manifest.json
  • examples/blog-with-auth/baseplate/snapshots/transactional/manifest.json
  • examples/todo-with-auth0/.baseplateignore
  • examples/todo-with-auth0/apps/admin/.baseplateignore
  • examples/todo-with-auth0/apps/backend/.baseplateignore
  • examples/todo-with-auth0/apps/web/.baseplateignore
  • examples/todo-with-auth0/baseplate/snapshots/admin/manifest.json
  • examples/todo-with-auth0/baseplate/snapshots/backend/manifest.json
  • examples/todo-with-auth0/baseplate/snapshots/root/manifest.json
  • examples/todo-with-auth0/baseplate/snapshots/web/manifest.json
  • knip.config.js
  • package.json
  • packages/core-generators/src/generators/node/prettier/prettier.generator.ts
  • packages/project-builder-cli/src/commands/sync.ts
  • packages/project-builder-dev/package.json
  • packages/project-builder-dev/src/commands/snapshot.ts
  • packages/project-builder-dev/src/commands/sync.ts
  • packages/project-builder-dev/src/commands/test-project.ts
  • packages/project-builder-dev/src/commands/test.ts
  • packages/project-builder-dev/src/e2e-runner/discover-tests.ts
  • packages/project-builder-dev/src/e2e-runner/environment.ts
  • packages/project-builder-dev/src/e2e-runner/errors.ts
  • packages/project-builder-dev/src/e2e-runner/generation-manifest.ts
  • packages/project-builder-dev/src/e2e-runner/index.ts
  • packages/project-builder-dev/src/e2e-runner/runner.ts
  • packages/project-builder-dev/src/e2e-runner/types.ts
  • packages/project-builder-dev/src/e2e-runner/utils/kill-process-group.ts
  • packages/project-builder-dev/src/e2e-runner/utils/ora.ts
  • packages/project-builder-dev/src/e2e-runner/utils/process.ts
  • packages/project-builder-dev/src/e2e-runner/utils/url.ts
  • packages/project-builder-dev/src/e2e-runner/utils/wait-for-signal.ts
  • packages/project-builder-dev/src/index.ts
  • packages/project-builder-server/src/actions/index.ts
  • packages/project-builder-server/src/actions/registry.ts
  • packages/project-builder-server/src/actions/snapshot/snapshot-add.action.ts
  • packages/project-builder-server/src/actions/snapshot/snapshot-remove.action.ts
  • packages/project-builder-server/src/actions/snapshot/snapshot-save.action.ts
  • packages/project-builder-server/src/actions/snapshot/snapshot-show.action.ts
  • packages/project-builder-server/src/actions/sync/sync-project.action.ts
  • packages/project-builder-server/src/actions/test-project/index.ts
  • packages/project-builder-server/src/actions/test-project/test-project-generate.action.ts
  • packages/project-builder-server/src/actions/test-project/test-project-init.action.ts
  • packages/project-builder-server/src/actions/test-project/test-project-paths.ts
  • packages/project-builder-server/src/actions/test-project/test-project-save.action.ts
  • packages/project-builder-server/src/actions/utils/project-discovery.ts
  • packages/project-builder-server/src/compiler/index.ts
  • packages/project-builder-server/src/diff/diff-project.ts
  • packages/project-builder-server/src/diff/snapshot/apply-diff-to-generator-output.ts
  • packages/project-builder-server/src/diff/snapshot/apply-diff-to-generator-output.unit.test.ts
  • packages/project-builder-server/src/diff/snapshot/create-snapshot-for-project.ts
  • packages/project-builder-server/src/diff/snapshot/index.ts
  • packages/project-builder-server/src/diff/snapshot/save-snapshot.ts
  • packages/project-builder-server/src/diff/snapshot/save-snapshot.unit.test.ts
  • packages/project-builder-server/src/diff/snapshot/snapshot-management.ts
  • packages/project-builder-server/src/diff/snapshot/snapshot-manifest.ts
  • packages/project-builder-server/src/diff/snapshot/snapshot-manifest.unit.test.ts
  • packages/project-builder-server/src/diff/snapshot/snapshot-types.ts
  • packages/project-builder-server/src/diff/snapshot/snapshot-utils.ts
  • packages/project-builder-server/src/diff/snapshot/snapshot-utils.unit.test.ts
  • packages/project-builder-server/src/index.ts
  • packages/project-builder-server/src/plugins/node-plugin-store.ts
  • packages/project-builder-server/src/project-definition/get-single-app-directory-for-project.ts
  • packages/project-builder-server/src/project-definition/load-project-definition.ts
  • packages/project-builder-server/src/sync/generate-for-directory.ts
  • packages/project-builder-server/src/sync/sync-project.ts
  • packages/project-builder-server/src/tests/helpers/generator-output.test-helper.ts
  • packages/project-builder-test/.gitignore
  • packages/project-builder-test/eslint.config.js
  • packages/project-builder-test/package.json
  • packages/project-builder-test/src/commands/generate.ts
  • packages/project-builder-test/src/commands/index.ts
  • packages/project-builder-test/src/commands/run.ts
  • packages/project-builder-test/src/commands/serve.ts
  • packages/project-builder-test/src/commands/test.ts
  • packages/project-builder-test/src/index.ts
  • packages/project-builder-test/src/runner/runner.ts
  • packages/project-builder-test/src/tests/simple.gen.ts
  • packages/project-builder-test/src/utils/console.ts
  • packages/project-builder-test/src/utils/directories.ts
  • packages/project-builder-test/src/utils/resolve.ts
  • packages/project-builder-test/src/utils/version.ts
  • packages/project-builder-test/test-projects/simple/project-definition.json
  • packages/project-builder-test/test-projects/simple/snapshots/backend/diffs/prisma_migrations_20260302151454_initial_migration_migration.sql.diff
  • packages/project-builder-test/test-projects/simple/snapshots/backend/diffs/prisma_migrations_migration_lock.toml.diff
  • packages/project-builder-test/test-projects/simple/snapshots/backend/diffs/src_prisma_seed.ts.diff
  • packages/project-builder-test/test-projects/simple/snapshots/backend/manifest.json
  • packages/project-builder-test/test-projects/simple/snapshots/root/diffs/pnpm-lock.yaml.diff
  • packages/project-builder-test/test-projects/simple/snapshots/root/manifest.json
  • packages/project-builder-test/test-projects/simple/snapshots/web/manifest.json
  • packages/project-builder-test/turbo.json
  • packages/sync/src/output/post-write-commands/run-commands.ts
  • packages/sync/src/utils/ignore-patterns.ts
  • packages/sync/src/utils/ignore-patterns.unit.test.ts
  • scripts/run-all.sh
💤 Files with no reviewable changes (15)
  • packages/project-builder-cli/src/commands/sync.ts
  • packages/project-builder-test/src/commands/run.ts
  • packages/project-builder-test/src/commands/test.ts
  • packages/project-builder-test/src/index.ts
  • packages/project-builder-test/src/utils/resolve.ts
  • packages/project-builder-test/src/utils/console.ts
  • packages/project-builder-test/src/utils/directories.ts
  • packages/project-builder-server/src/project-definition/get-single-app-directory-for-project.ts
  • packages/project-builder-test/src/commands/index.ts
  • packages/project-builder-test/src/commands/generate.ts
  • packages/sync/src/utils/ignore-patterns.ts
  • packages/project-builder-test/turbo.json
  • packages/project-builder-test/src/runner/runner.ts
  • packages/project-builder-test/src/commands/serve.ts
  • packages/project-builder-test/src/utils/version.ts

Comment thread packages/project-builder-dev/src/commands/snapshot.ts Outdated
Comment thread packages/project-builder-dev/src/e2e-runner/discover-tests.ts Outdated
Comment thread packages/project-builder-dev/src/e2e-runner/generation-manifest.ts
Comment thread packages/project-builder-dev/src/e2e-runner/generation-manifest.ts
Comment thread packages/project-builder-server/src/actions/snapshot/snapshot-show.action.ts Outdated
Comment on lines +20 to +25
baseplateDirectory: z
.string()
.optional()
.describe('Directory containing snapshot to use when generating.'),
.describe(
'Custom baseplate directory for snapshot resolution. Defaults to <projectDirectory>/baseplate.',
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Backward-compatibility regression risk in renamed input field.

On Line 20 and Line 57, replacing snapshotDirectory with baseplateDirectory without alias handling can silently break older callers (unknown keys are ignored by default), and Line 92 then uses fallback resolution unintentionally.

💡 Migration-safe fix (support deprecated alias during transition)
 const syncProjectInputSchema = z.object({
   project: z.string().describe('The name or ID of the project to sync.'),
@@
   baseplateDirectory: z
     .string()
     .optional()
     .describe(
       'Custom baseplate directory for snapshot resolution. Defaults to <projectDirectory>/baseplate.',
     ),
+  snapshotDirectory: z
+    .string()
+    .optional()
+    .describe('DEPRECATED: use baseplateDirectory.'),
   packages: z
     .array(z.string())
     .optional()
     .describe('Only sync specific packages by name.'),
 });
@@
     const {
       project: projectId,
       overwrite,
       skipCommands,
       baseplateDirectory,
+      snapshotDirectory,
       packages,
     } = input;
+    const resolvedBaseplateDirectory =
+      baseplateDirectory ?? snapshotDirectory;
@@
-        baseplateDirectory,
+        baseplateDirectory: resolvedBaseplateDirectory,

Also applies to: 57-57, 92-92

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-server/src/actions/sync/sync-project.action.ts`
around lines 20 - 25, The schema rename from snapshotDirectory to
baseplateDirectory will break callers; update the input handling for the sync
project action so both keys are accepted during migration: keep the existing zod
field baseplateDirectory but also accept the deprecated snapshotDirectory
(alias) and, if snapshotDirectory is provided, map/copy its value into
baseplateDirectory before any fallback resolution logic (the code that currently
reads baseplateDirectory and applies the fallback on missing value). Reference
the schema field baseplateDirectory and the old key snapshotDirectory and ensure
the resolution code that computes the final directory (the early fallback logic
currently triggered around the resolution step) uses the normalized
baseplateDirectory value.

Comment on lines 151 to 166
logger.info('Compiling applications...');
const apps = compilePackages(projectJson, context);

// Filter apps if specified
const app = apps.find((app) => appName === app.name);

if (!app) {
throw new Error(
`No applications found named ${appName}. Available apps: ${apps.map((a) => a.name).join(', ')}`,
);
let appsToProcess: PackageEntry[];

if (appName) {
const app = apps.find((a) => appName === a.name);
if (!app) {
throw new Error(
`No applications found named ${appName}. Available apps: ${apps.map((a) => a.name).join(', ')}`,
);
}
appsToProcess = [app];
} else {
appsToProcess = apps;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

App selection currently includes non-app packages.

appsToProcess is derived from compilePackages(...), which includes more than apps. In all-app mode this can create snapshots for root/libs, and in single-app mode a non-app name can be selected.

🐛 Suggested fix
-    const apps = compilePackages(projectJson, context);
+    const packages = compilePackages(projectJson, context);
+    const appNameSet = new Set(projectJson.apps.map((a) => a.name));
+    const apps = packages.filter((pkg) => appNameSet.has(pkg.name));

@@
-      const app = apps.find((a) => appName === a.name);
+      const app = apps.find((a) => appName === a.name);
       if (!app) {
         throw new Error(
           `No applications found named ${appName}. Available apps: ${apps.map((a) => a.name).join(', ')}`,
         );
       }
       appsToProcess = [app];
     } else {
       appsToProcess = apps;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/project-builder-server/src/diff/snapshot/create-snapshot-for-project.ts`
around lines 151 - 166, The compiled list from compilePackages(...) can include
non-app packages, so ensure appsToProcess only contains application packages:
after const apps = compilePackages(projectJson, context) filter that array
(e.g., const appsOnly = apps.filter(isApp)) where isApp checks the PackageEntry
marker for an application (package type/manifest flag used in your codebase),
then use appsOnly for the appName lookup and as the default list (replace uses
of apps with appsOnly when computing appsToProcess and validating appName). Also
add a clear error when appName is provided but matches a non-app package by
searching appsOnly rather than apps.

Comment on lines +95 to +106
if (manifest.files.added.some((entry) => entry.path === filePath)) {
return manifest;
}

return {
...manifest,
files: {
...manifest.files,
added: [...manifest.files.added, filePath],
added: [
...manifest.files.added.filter((entry) => entry.path !== filePath),
{ path: filePath, ...(contentFile && { contentFile }) },
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Allow addAddedFile to upgrade an existing entry with contentFile.

Current duplicate handling returns early, so a later call cannot enrich { path } into { path, contentFile }. That can leave incomplete metadata in the manifest.

Proposed fix
 function addAddedFile(
   manifest: SnapshotManifest,
   filePath: string,
   contentFile?: string,
 ): SnapshotManifest {
-  if (manifest.files.added.some((entry) => entry.path === filePath)) {
-    return manifest;
-  }
+  const existing = manifest.files.added.find((entry) => entry.path === filePath);
+  if (existing) {
+    if (!contentFile || existing.contentFile === contentFile) {
+      return manifest;
+    }
+    return {
+      ...manifest,
+      files: {
+        ...manifest.files,
+        added: manifest.files.added.map((entry) =>
+          entry.path === filePath ? { ...entry, contentFile } : entry,
+        ),
+      },
+    };
+  }

   return {
     ...manifest,
     files: {
       ...manifest.files,
       added: [
-        ...manifest.files.added.filter((entry) => entry.path !== filePath),
         { path: filePath, ...(contentFile && { contentFile }) },
+        ...manifest.files.added,
       ],
     },
   };
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (manifest.files.added.some((entry) => entry.path === filePath)) {
return manifest;
}
return {
...manifest,
files: {
...manifest.files,
added: [...manifest.files.added, filePath],
added: [
...manifest.files.added.filter((entry) => entry.path !== filePath),
{ path: filePath, ...(contentFile && { contentFile }) },
],
function addAddedFile(
manifest: SnapshotManifest,
filePath: string,
contentFile?: string,
): SnapshotManifest {
const existing = manifest.files.added.find((entry) => entry.path === filePath);
if (existing) {
if (!contentFile || existing.contentFile === contentFile) {
return manifest;
}
return {
...manifest,
files: {
...manifest.files,
added: manifest.files.added.map((entry) =>
entry.path === filePath ? { ...entry, contentFile } : entry,
),
},
};
}
return {
...manifest,
files: {
...manifest.files,
added: [
{ path: filePath, ...(contentFile && { contentFile }) },
...manifest.files.added,
],
},
};
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-server/src/diff/snapshot/snapshot-manifest.ts`
around lines 95 - 106, The current early-return when manifest.files.added
contains an entry with the same path prevents enriching an existing entry with
contentFile; update addAddedFile so it does not return immediately but instead
maps manifest.files.added and replaces the matching entry with one that includes
contentFile when provided (e.g., in the block that currently checks
manifest.files.added.some((entry) => entry.path === filePath), change behavior
to transform the existing entry to { path: filePath, ...(contentFile && {
contentFile }) } rather than returning manifest), ensuring new entries are still
appended when no match is found and preserving other manifest fields.

@kingston kingston merged commit 2f860c5 into main Mar 2, 2026
12 checks passed
@kingston kingston deleted the kingston/eng-1040-testing-infra-snapshot-diff-storage-enhancement branch March 2, 2026 17:02
@github-actions github-actions Bot mentioned this pull request Mar 2, 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.

1 participant