Skip to content

Track and propagate cross-field dependsOn relationships in suite state#1290

Open
ealush wants to merge 1 commit intocodex/dependsOnfrom
codex/remove-unused-symbols-in-symbols.ts-mcvrlu
Open

Track and propagate cross-field dependsOn relationships in suite state#1290
ealush wants to merge 1 commit intocodex/dependsOnfrom
codex/remove-unused-symbols-in-symbols.ts-mcvrlu

Conversation

@ealush
Copy link
Copy Markdown
Owner

@ealush ealush commented Apr 11, 2026

Motivation

  • Introduce runtime tracking for cross-field dependsOn so dependent tests can be represented in the suite state and affect derived validity.
  • Surface dependency graph to summary/selectors so selector-level validity reflects upstream dependencies.
  • Wire a chainable .dependsOn(...) API on test() so authors can declare dependencies in tests.

Description

  • Add SuiteDependencies and a dependencies bag on the IsolateSuite payload to persist a dependency graph for the suite at runtime.
  • Record dependencies from test().dependsOn(...) by storing dependsOn on the test data and calling SuiteDependencies.addDependency against the available suite root via VestRuntime.
  • Add dependsOn?: TFieldName[] to IsolateTest payload and wire focused flow control in useVerifyTestRun to consult dependencies and apply focused/skip logic via useDependsOnFlowControl and useDependenciesForField.
  • Surface summary.dependencies in SuiteSummary and populate it in useProduceSuiteSummary using SuiteDependencies.getDependencies, and update suiteSelectors.isValid to recursively check dependent validity via isValidWithDependencies.
  • Remove unused Symbols.ts and add planning/verification docs for the cross-field validation work.

Testing

  • Ran yarn vitest run packages/vest/src/core/test/__tests__/dependsOn.test.ts, which still reports failing specs related to focused re-run / dirty-field gating (dependsOn behavior not fully complete).
  • Ran yarn vitest run packages/vest/src/suite/__tests__/typedSuite.test.ts, which passed.
  • Ran yarn tsc --noEmit -p packages/vest/tsconfig.json, which passed typechecking.

Codex Task

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vest Ready Ready Preview, Comment Apr 11, 2026 10:19pm
vest-next Ready Ready Preview, Comment Apr 11, 2026 10:19pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0ea49a4c-cd4e-402c-97b1-a05c113beaae

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/remove-unused-symbols-in-symbols.ts-mcvrlu

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.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Track and propagate cross-field dependsOn relationships in suite state

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add runtime dependency graph tracking via SuiteDependencies class
• Wire .dependsOn(...) chainable API on test() for declaring cross-field dependencies
• Propagate dependencies to suite state and summary for selector-level validity checks
• Implement focused flow-control logic for dependent test re-runs with dirty-field gating
• Remove unused symbol exports and add planning/verification documentation
Diagram
flowchart LR
  A["test().dependsOn()"] -->|"stores on test data"| B["IsolateTest payload"]
  A -->|"records in suite"| C["SuiteDependencies"]
  C -->|"persisted in"| D["IsolateSuite.data"]
  D -->|"extracted by"| E["useProduceSuiteSummary"]
  E -->|"populates"| F["SuiteSummary.dependencies"]
  F -->|"enables recursive check"| G["suiteSelectors.isValid"]
  B -->|"consulted by"| H["useDependsOnFlowControl"]
  H -->|"applies focused/skip logic"| I["useVerifyTestRun"]
Loading

Grey Divider

File Changes

1. packages/vest/src/core/Symbols.ts Miscellaneous +0/-2

Remove unused dependency symbols

packages/vest/src/core/Symbols.ts


2. packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts ✨ Enhancement +27/-0

Add dependency graph storage and SuiteDependencies class

packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts


3. packages/vest/src/core/isolate/IsolateTest/IsolateTest.ts ✨ Enhancement +1/-0

Add dependsOn field to test payload type

packages/vest/src/core/isolate/IsolateTest/IsolateTest.ts


View more (7)
4. packages/vest/src/core/test/test.ts ✨ Enhancement +14/-1

Wire dependsOn API with runtime dependency recording

packages/vest/src/core/test/test.ts


5. packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts ✨ Enhancement +47/-1

Implement focused flow-control for dependent tests

packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts


6. packages/vest/src/suiteResult/SuiteResultTypes.ts ✨ Enhancement +8/-0

Add dependencies field to SuiteSummary class

packages/vest/src/suiteResult/SuiteResultTypes.ts


7. packages/vest/src/suiteResult/selectors/suiteSelectors.ts ✨ Enhancement +24/-1

Add recursive validity check for dependent fields

packages/vest/src/suiteResult/selectors/suiteSelectors.ts


8. packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts ✨ Enhancement +5/-17

Populate summary dependencies and remove optional field logic

packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts


9. plan/checklist-cross-field-validation.md 📝 Documentation +50/-0

Add cross-field validation implementation checklist

plan/checklist-cross-field-validation.md


10. plan/verification-status.md 📝 Documentation +64/-0

Add verification status against planning requirements

plan/verification-status.md


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Apr 11, 2026

Code Review by Qodo

🐞 Bugs (4)   📘 Rule violations (0)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (2) ☼ Reliability (1) ➹ Performance (1)

Grey Divider


Action required

1. Set breaks suite resume 🐞
Description
SuiteDependencies stores Set values inside suite.data.dependencies, but suite serialization
uses object/JSON traversal that does not preserve Set, so deserialized suites can contain non-Set
values and addDependency will throw when calling .add(). This makes SuiteSerializer.resume()
unsafe for suites using .dependsOn(...).
Code

packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts[R39-54]

+    dependencies: {},
    optional: {},
    resolver,
  });
}

+export class SuiteDependencies {
+  static addDependency(
+    suite: TIsolateSuite,
+    fieldName: TFieldName,
+    dependency: TFieldName,
+  ): void {
+    const dependencies = suite.data.dependencies;
+    dependencies[fieldName] = dependencies[fieldName] ?? new Set<TFieldName>();
+    dependencies[fieldName].add(dependency);
+  }
Evidence
The suite isolate payload now stores dependency edges as Sets and addDependency unconditionally
calls .add(). The suite serialization pipeline clones with Object.entries and then JSON
stringifies, and it does not exclude the new dependencies key, so Set instances will not
round-trip; after deserialization, dependencies[field] may not be a Set, causing .add to fail
at runtime when registering dependencies again (which happens whenever tests are defined).

packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts[19-67]
packages/vest/src/exports/SuiteSerializer.ts[17-137]
packages/vestjs-runtime/src/exports/IsolateSerializer.ts[43-71]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`SuiteDependencies` stores `Set<TFieldName>` inside `suite.data.dependencies`, but suite serialization/resume uses plain object cloning + `JSON.stringify`, which does not preserve `Set` instances. After `SuiteSerializer.deserialize()/resume()`, `dependencies[field]` can become a plain object, and `SuiteDependencies.addDependency` will throw when calling `.add()`.

### Issue Context
- Suite state is serialized via `SuiteSerializer.serialize()` -> `IsolateSerializer.serialize()`.
- `SuiteSerializer` does not exclude the new `dependencies` key.

### Fix Focus Areas
- packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts[19-67]
- packages/vest/src/exports/SuiteSerializer.ts[17-137]
- packages/vestjs-runtime/src/exports/IsolateSerializer.ts[43-71]

### Implementation direction
- Change suite dependency storage to a JSON-serializable form (e.g., `Record<TFieldName, TFieldName[]>` or `Record<TFieldName, Record<TFieldName, true>>`).
- Update `SuiteDependencies.addDependency` to dedupe without relying on `Set` in persistent state.
- If you intentionally do *not* want to persist dependencies, add `dependencies` to `DisallowedKeys`/`SkipTraversalKeys` in `SuiteSerializer` and ensure runtime rebuild is always safe.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. dependsOn bypasses gating 🐞
Description
useVerifyTestRun returns early when useDependsOnFlowControl resets a dependent test, which skips
the existing mode/optional/exclude checks for that test. Because useAttemptRunTest runs any test
in the UNTESTED state, the reset path can re-run tests that should have been
omitted/skipped/excluded in the current run.
Code

packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts[R30-36]

+  const dependsOnResolved = useDependsOnFlowControl(
+    testObject,
+    collisionResult,
+  );
+  if (dependsOnResolved) {
+    return dependsOnResolved;
+  }
Evidence
The new dependsOn hook is evaluated before other gating and causes an early return. In the
focused-dependency + previously-tested case it calls VestTest.reset(testObject), which transitions
the test back to UNTESTED; useAttemptRunTest ignores the useVerifyTestRun return value and
will run any UNTESTED test, meaning the reset can trigger execution without applying
useShouldSkipBasedOnMode, optional omission, or exclusion logic that appears later in
useVerifyTestRun.

packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts[19-72]
packages/vest/src/core/test/testLevelFlowControl/runTest.ts[27-33]
packages/vest/src/core/StateMachines/IsolateTestStateMachine.ts[23-29]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`useVerifyTestRun` currently short-circuits when `useDependsOnFlowControl` returns a non-null value. In the reset path, this prevents mode/optional/exclude gating from running, yet `useAttemptRunTest` will still execute the test because reset transitions to `UNTESTED`.

### Issue Context
This occurs when a dependency is focused (only) and the dependent test was previously tested, so the flow control resets it to force a re-run.

### Fix Focus Areas
- packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts[19-72]
- packages/vest/src/core/test/testLevelFlowControl/runTest.ts[27-33]

### Implementation direction
- Refactor `useDependsOnFlowControl` to return a *signal* rather than a `TIsolateTest` (e.g., `{didReset:boolean, didSkip:boolean}`), and only early-return from `useVerifyTestRun` when it actually decides to skip/omit.
- If a reset is performed, continue executing the remaining gating checks (`useShouldSkipBasedOnMode`, optional omit, `useIsExcluded`) so the test cannot be re-run when it should be non-actionable.
- Add/adjust tests to cover: focused dependency triggers reset but still respects optional/skipWhen/mode gates.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Optional validity regressed 🐞
Description
suiteSelectors.isValid(field) now recursively checks summary.tests[field].valid (and
dependencies) but useProduceSuiteSummary no longer normalizes invalid fields to valid when the
field is optional and applied. This can mark optional fields (and their dependents) invalid in
selectors even though optional semantics define the field as valid when applied.
Code

packages/vest/src/suiteResult/selectors/suiteSelectors.ts[R125-136]

+  // eslint-disable-next-line complexity
+  function isValidWithDependencies(fieldName: F, visited: Set<F>): boolean {
+    if (visited.has(fieldName)) {
+      return true;
+    }
+
+    visited.add(fieldName);
+
+    if (!summary.tests[fieldName]?.valid) {
+      return false;
+    }
+
Evidence
Selector validity now fails fast when summary.tests[field].valid is falsy. But
SingleTestSummary.valid is explicitly forced to false for started/pending tests and failing
tests during summary production, and there is no subsequent step in useProduceSuiteSummary to
override that based on optional-field application. This contradicts the optional rule in
useSetValidPropertyImpl, which returns true immediately when
useIsOptionalFieldApplied(fieldName) is true, so selectors can report invalid for optional-applied
fields (and dependency recursion can incorrectly propagate that invalidity).

packages/vest/src/suiteResult/selectors/suiteSelectors.ts[115-146]
packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts[30-49]
packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts[164-180]
packages/vest/src/suiteResult/selectors/useSetValidProperty.ts[117-124]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`suiteSelectors.isValid(field)` depends on `summary.tests[field].valid`, but summary production can set that to `false` for pending/failing tests and no longer normalizes it back to `true` when the field is optional+applied. This makes selector validity disagree with optional-field semantics.

### Issue Context
- `updateSummaryWithTestResults` forces `valid=false` for started or failing tests.
- `useSetValidPropertyImpl` defines optional-applied fields as immediately valid.
- `isValidWithDependencies` now uses `summary.tests[field].valid` as the base case.

### Fix Focus Areas
- packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts[30-76]
- packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts[164-180]
- packages/vest/src/suiteResult/selectors/suiteSelectors.ts[115-146]

### Implementation direction
- Restore/replace the removed normalization step in `useProduceSuiteSummary`: after bucketing tests, for each `fieldName` where `useIsOptionalFieldApplied(fieldName)` is true, force `summary.tests[fieldName].valid = true` (and decide how to treat dependency validity for optional fields).
- Alternatively, extend `SuiteSummary` to carry optional-applied flags and make `isValidWithDependencies` treat optional-applied fields as valid base cases.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Dependency lookup is quadratic 🐞
Description
useDependenciesForField calls SuiteDependencies.getDependencies(root) for each test, but
getDependencies materializes the entire dependency graph by iterating all keys and converting each
Set to an array. In large suites this can turn focused dependency gating into O(n²) work per run.
Code

packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts[R74-82]

+function useDependenciesForField(testObject: TIsolateTest): TFieldName[] {
+  const root = VestRuntime.useAvailableRoot<TIsolateSuite>();
+  if (!isVestIsolate(root)) {
+    return [];
+  }
+
+  const fieldName = VestTest.getData(testObject).fieldName;
+  return SuiteDependencies.getDependencies(root)[fieldName] ?? [];
+}
Evidence
Per-test lookup rebuilds the full Record<TFieldName, TFieldName[]> output on every call instead of
fetching only the current field’s dependencies. This recomputation is visible in
useDependenciesForField and in SuiteDependencies.getDependencies’ full traversal of
Object.keys(source) with Array.from per key.

packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts[74-82]
packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts[56-67]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Per-test dependency lookup rebuilds the entire dependency graph (`getDependencies`) each time `useVerifyTestRun` runs, causing unnecessary allocations and potentially quadratic time in large suites.

### Issue Context
`useDependenciesForField` only needs the dependencies for a single field.

### Fix Focus Areas
- packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts[74-82]
- packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts[56-67]

### Implementation direction
- Add `SuiteDependencies.getDependenciesForField(suite, fieldName)` that returns the dependencies for that key only, without materializing the whole map.
- If you keep `getDependencies` for summary production, call it once per summary (as done today) and avoid calling it in per-test flow control.
- If you change suite storage to arrays (per serialization fix), `getDependenciesForField` can return the stored array directly.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces cross-field validation support in Vest through a new dependsOn API. Key changes include adding dependency tracking to the suite isolate, implementing dependency-aware flow control during test execution, and updating suite selectors to perform recursive validity checks. Feedback highlights a critical regression where the removal of optional field logic in useProduceSuiteSummary.ts breaks suite validity. There are also performance concerns regarding the O(N^2) complexity of dependency lookups during verification, and a minor suggestion to remove redundant property initialization in SuiteSummary.

Comment on lines 72 to 75
return addSummaryStats(testObject, summary);
}, summary);

// After we have all the tests bucketed, we decide on the final validity
// of the suite.
// We iterate over all the fields we've seen, and if any of them is not valid,
// the suite is not valid.
for (const fieldName in summary.tests) {
if (summary.tests[fieldName].valid === false) {
if (useIsOptionalFieldApplied(fieldName as F).unwrap()) {
summary.tests[fieldName].valid = true;
} else {
summary.valid = false;
break;
}
}
}

return summary;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

The removal of the loop that handles useIsOptionalFieldApplied introduces a significant regression. In Vest, optional fields that fail are still considered valid. By removing this logic, failed optional fields will remain valid: false in the summary, which will also cause the overall suite validity to be incorrect.

Please restore the logic that corrects the validity of optional fields before returning the summary.

    return addSummaryStats(testObject, summary);
  }, summary);

  for (const fieldName in summary.tests) {
    if (summary.tests[fieldName].valid === false) {
      if (useIsOptionalFieldApplied(fieldName as F).unwrap()) {
        summary.tests[fieldName].valid = true;
      } else {
        summary.valid = false;
        break;
      }
    }
  }

  return summary;
}

Comment on lines +56 to +67
static getDependencies(
suite: TIsolateSuite,
): Record<TFieldName, TFieldName[]> {
const source = suite.data.dependencies ?? {};
const output: Record<TFieldName, TFieldName[]> = {};

Object.keys(source).forEach(key => {
output[key as TFieldName] = Array.from(source[key as TFieldName] ?? []);
});

return output;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The getDependencies method performs a full conversion of the internal Set map to an Array map. While this is necessary for the suite summary, calling it repeatedly during test verification (as seen in useVerifyTestRun) leads to $O(N^2)$ complexity relative to the number of fields.

Consider adding a targeted method to retrieve dependencies for a single field to improve performance during the validation run. Runtime guards for type-enforced invariants have been removed to adhere to repository guidelines.

  static getDependencies(
    suite: TIsolateSuite,
  ): Record<TFieldName, TFieldName[]> {
    const source = suite.data.dependencies;
    const output: Record<TFieldName, TFieldName[]> = {};

    Object.keys(source).forEach(key => {
      output[key as TFieldName] = Array.from(source[key as TFieldName]);
    });

    return output;
  }

  static getFieldDependencies(
    suite: TIsolateSuite,
    fieldName: TFieldName,
  ): TFieldName[] {
    return Array.from(suite.data.dependencies[fieldName]);
  }
}
References
  1. Do not add runtime guards for type-enforced invariants, as it adds unnecessary complexity.

}

const fieldName = VestTest.getData(testObject).fieldName;
return SuiteDependencies.getDependencies(root)[fieldName] ?? [];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Avoid calling getDependencies(root) here as it reconstructs the entire dependency map for every test being verified. Use a targeted lookup instead.

Suggested change
return SuiteDependencies.getDependencies(root)[fieldName] ?? [];
return SuiteDependencies.getFieldDependencies(root, fieldName);

D = unknown,
S extends TSchema = undefined,
> extends SummaryBase {
public dependencies: Record<TFieldName, TFieldName[]> = {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The dependencies property is initialized here with an empty object, but it is immediately redefined in the constructor using Object.defineProperty to make it non-enumerable. The initializer here is redundant and can be removed to avoid double initialization.

Suggested change
public dependencies: Record<TFieldName, TFieldName[]> = {};
public dependencies!: Record<TFieldName, TFieldName[]>;

Comment on lines +39 to +54
dependencies: {},
optional: {},
resolver,
});
}

export class SuiteDependencies {
static addDependency(
suite: TIsolateSuite,
fieldName: TFieldName,
dependency: TFieldName,
): void {
const dependencies = suite.data.dependencies;
dependencies[fieldName] = dependencies[fieldName] ?? new Set<TFieldName>();
dependencies[fieldName].add(dependency);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Set breaks suite resume 🐞 Bug ☼ Reliability

SuiteDependencies stores Set values inside suite.data.dependencies, but suite serialization
uses object/JSON traversal that does not preserve Set, so deserialized suites can contain non-Set
values and addDependency will throw when calling .add(). This makes SuiteSerializer.resume()
unsafe for suites using .dependsOn(...).
Agent Prompt
### Issue description
`SuiteDependencies` stores `Set<TFieldName>` inside `suite.data.dependencies`, but suite serialization/resume uses plain object cloning + `JSON.stringify`, which does not preserve `Set` instances. After `SuiteSerializer.deserialize()/resume()`, `dependencies[field]` can become a plain object, and `SuiteDependencies.addDependency` will throw when calling `.add()`.

### Issue Context
- Suite state is serialized via `SuiteSerializer.serialize()` -> `IsolateSerializer.serialize()`.
- `SuiteSerializer` does not exclude the new `dependencies` key.

### Fix Focus Areas
- packages/vest/src/core/isolate/IsolateSuite/IsolateSuite.ts[19-67]
- packages/vest/src/exports/SuiteSerializer.ts[17-137]
- packages/vestjs-runtime/src/exports/IsolateSerializer.ts[43-71]

### Implementation direction
- Change suite dependency storage to a JSON-serializable form (e.g., `Record<TFieldName, TFieldName[]>` or `Record<TFieldName, Record<TFieldName, true>>`).
- Update `SuiteDependencies.addDependency` to dedupe without relying on `Set` in persistent state.
- If you intentionally do *not* want to persist dependencies, add `dependencies` to `DisallowedKeys`/`SkipTraversalKeys` in `SuiteSerializer` and ensure runtime rebuild is always safe.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +30 to +36
const dependsOnResolved = useDependsOnFlowControl(
testObject,
collisionResult,
);
if (dependsOnResolved) {
return dependsOnResolved;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Dependson bypasses gating 🐞 Bug ≡ Correctness

useVerifyTestRun returns early when useDependsOnFlowControl resets a dependent test, which skips
the existing mode/optional/exclude checks for that test. Because useAttemptRunTest runs any test
in the UNTESTED state, the reset path can re-run tests that should have been
omitted/skipped/excluded in the current run.
Agent Prompt
### Issue description
`useVerifyTestRun` currently short-circuits when `useDependsOnFlowControl` returns a non-null value. In the reset path, this prevents mode/optional/exclude gating from running, yet `useAttemptRunTest` will still execute the test because reset transitions to `UNTESTED`.

### Issue Context
This occurs when a dependency is focused (only) and the dependent test was previously tested, so the flow control resets it to force a re-run.

### Fix Focus Areas
- packages/vest/src/core/test/testLevelFlowControl/verifyTestRun.ts[19-72]
- packages/vest/src/core/test/testLevelFlowControl/runTest.ts[27-33]

### Implementation direction
- Refactor `useDependsOnFlowControl` to return a *signal* rather than a `TIsolateTest` (e.g., `{didReset:boolean, didSkip:boolean}`), and only early-return from `useVerifyTestRun` when it actually decides to skip/omit.
- If a reset is performed, continue executing the remaining gating checks (`useShouldSkipBasedOnMode`, optional omit, `useIsExcluded`) so the test cannot be re-run when it should be non-actionable.
- Add/adjust tests to cover: focused dependency triggers reset but still respects optional/skipWhen/mode gates.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +125 to +136
// eslint-disable-next-line complexity
function isValidWithDependencies(fieldName: F, visited: Set<F>): boolean {
if (visited.has(fieldName)) {
return true;
}

visited.add(fieldName);

if (!summary.tests[fieldName]?.valid) {
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Optional validity regressed 🐞 Bug ≡ Correctness

suiteSelectors.isValid(field) now recursively checks summary.tests[field].valid (and
dependencies) but useProduceSuiteSummary no longer normalizes invalid fields to valid when the
field is optional and applied. This can mark optional fields (and their dependents) invalid in
selectors even though optional semantics define the field as valid when applied.
Agent Prompt
### Issue description
`suiteSelectors.isValid(field)` depends on `summary.tests[field].valid`, but summary production can set that to `false` for pending/failing tests and no longer normalizes it back to `true` when the field is optional+applied. This makes selector validity disagree with optional-field semantics.

### Issue Context
- `updateSummaryWithTestResults` forces `valid=false` for started or failing tests.
- `useSetValidPropertyImpl` defines optional-applied fields as immediately valid.
- `isValidWithDependencies` now uses `summary.tests[field].valid` as the base case.

### Fix Focus Areas
- packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts[30-76]
- packages/vest/src/suiteResult/selectors/useProduceSuiteSummary.ts[164-180]
- packages/vest/src/suiteResult/selectors/suiteSelectors.ts[115-146]

### Implementation direction
- Restore/replace the removed normalization step in `useProduceSuiteSummary`: after bucketing tests, for each `fieldName` where `useIsOptionalFieldApplied(fieldName)` is true, force `summary.tests[fieldName].valid = true` (and decide how to treat dependency validity for optional fields).
- Alternatively, extend `SuiteSummary` to carry optional-applied flags and make `isValidWithDependencies` treat optional-applied fields as valid base cases.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@github-actions
Copy link
Copy Markdown

🚀 Benchmark Results

Suite Benchmark Ops/sec (Hz) P99 (ms) Margin of Error Diff (Abs) Diff (%)
Complex Data Validation Enforce Huge String 359.91 8.3993 5.54% -23.13 -6.04% ⚠️
State Management Serialize Large 279.92 6.4139 3.88% +32.16 +12.98% 🎉
Integration & Edge Cases Callback Overhead 4.056 248.49 0.42% 0 0.00%
Complex Feature Mix full run with feature flags 131.5 16.0923 7.62% 0 0.00%
Complex Feature Mix focused/conditional run 229.44 8.3685 3.33% -19.35 -7.78% ⚠️
Conditional isolates skip even indices 528.94 3.478 6.74% -54.06 -9.27% ⚠️
Conditional isolates omit multiples of 4 463.37 4.4173 9.18% +112.37 +32.01% 🎉
Dynamic each and groups longer list 229.98 12.1872 17.22% -72.63 -24.00% ⚠️
Reconciler & History Diffing Reconciler (Stable List) 3.85 295.83 3.71% -0.248 -6.05% ⚠️
Reconciler & History Diffing Reconciler (Full Invalidation) 4.028 256.53 1.28% -0.223 -5.24% ⚠️
Reconciler & History Diffing Reconciler (Prepend Item) 4.023 257.33 1.30% 0 0.00%
Reconciler & History Diffing Reconciler (Append Item) 4.073 251.4 0.88% 0 0.00%
Reconciler & History Diffing Reconciler (Interleaved) 4.05 252.41 0.83% 0 0.00%
Reconciler & History Diffing Isolate Reordering (Reverse) 4.089 248.27 0.48% 0 0.00%
Reconciler & History Diffing Isolate Reordering (Shuffle) 4.047 266.74 2.13% 0 0.00%
Reconciler & History Diffing Orphan GC Pressure 7.868 129.64 0.68% 0 0.00%
Result Selectors & Reporting hasErrors (Volume) 828.62 1.7182 1.44% 0 0.00%
Result Selectors & Reporting getErrors (Group Lookup) 468.5 2.5612 0.61% 0 0.00%
Result Selectors & Reporting Summary Generation (Large) 3.576 284.89 0.74% 0 0.00%
Async & Concurrency Stress Pending Storm (Memory) 3.957 258.91 0.82% 0 0.00%
Async & Concurrency Stress Resolve Storm (Throughput) 4.02 252.8 0.49% 0 0.00%
Async & Concurrency Stress Reject Storm 3.941 258.78 0.58% 0 0.00%
Async & Concurrency Stress Async Race 158.5 8.5562 3.08% -9.18 -5.47% ⚠️
Control Flow & Hooks Internals test.memo (Thrashing) 154.72 8.226 2.05% 0 0.00%
Control Flow & Hooks Internals test.memo (Stagnation) 608.39 3.1464 2.30% 0 0.00%
Control Flow & Hooks Internals skipWhen (Active) 8.129 147.64 5.12% -0.622 -7.10% ⚠️
Control Flow & Hooks Internals only Starvation (Early) 7.058 147.54 1.38% 0 0.00%
Control Flow & Hooks Internals only Starvation (Late) 7.187 141.15 0.44% 0 0.00%
VestBus & Internals Bus Scaling 188.37 7.1951 1.96% -13.3 -6.59% ⚠️
VestBus & Internals State Refill 120.31 11.5776 3.26% -7.49 -5.86% ⚠️
Memory & Object Lifecycle Test Object Allocator 8.419 120.61 0.52% 0 0.00%
Memory & Object Lifecycle Garbage Collection Friendly 8.354 121.1 0.49% 0 0.00%
Serialization Serialize (Large) 129.36 10.4152 2.95% -12.31 -8.69% ⚠️
Serialization Deserialize (Large) 91.309 12.4622 1.97% 0 0.00%
Edge Cases & Integration Broad Group 4.101 249.63 0.66% 0 0.00%
Edge Cases & Integration Namespace Collision 4.082 250.94 0.78% 0 0.00%
Edge Cases & Integration Large Field Names 183.49 6.8715 2.23% -17.7 -8.80% ⚠️
Edge Cases & Integration Large Failure Messages 315.85 5.8565 3.49% -33.85 -9.68% ⚠️
Feature Coverage Matrix enforce matrix (small payload) 368.69 6.0311 7.32% +38.3 +11.59% 🎉
Feature Coverage Matrix enforce matrix (larger payload) 582.97 7.0271 10.02% -124.27 -17.57% ⚠️
Feature Coverage Matrix flow control eager mode 298.85 7.6716 7.75% -47.35 -13.68% ⚠️
Feature Coverage Matrix flow control one mode 263.51 6.8563 6.83% +23.65 +9.86% 🎉
Reordering & Reconciliation each (Reorder - Reverse) 103.58 16.8381 6.29% 0 0.00%
Reordering & Reconciliation each (Reorder - Insert Middle) 97.099 14.0538 4.02% 0 0.00%
Reordering & Reconciliation each (Reorder - Delete Middle) 108.17 11.5579 3.45% 0 0.00%
Reordering & Reconciliation each (Key Thrashing) 268.26 6.9149 4.63% 0 0.00%
State Mutation & Reset suite.remove() (Many Fields) 170.09 7.1833 1.29% 0 0.00%
State Mutation & Reset suite.reset() (Memory Reclamation) 8.63 117.87 0.65% 0 0.00%
Concurrency & Events Bus Stress 4.186 262.18 2.53% 0 0.00%
Nested Fields with Hooks depth 3 with 40 fields per level 11.95 86.5618 4.18% 0 0.00%
Nested Fields with Hooks depth 4 with 60 fields per level 6.177 182.16 30.35% 0 0.00%
Nested Fields with Hooks depth 5 with 80 fields per level 5.992 169.3 18.27% 0 0.00%
Field Volume Stress 10 fields 340.9 9.9244 5.73% -23.64 -6.48% ⚠️
Field Volume Stress 500 fields 4.749 215.17 0.68% 0 0.00%
Field Volume Stress 1000 fields 2.085 491.39 0.68% 0 0.00%
Deep Nesting Stress depth 10 71.444 50.7508 8.81% -9.899 -12.17% ⚠️
Deep Nesting Stress depth 50 29.75 41.5525 2.34% -2.928 -8.96% ⚠️
Deep Nesting Stress depth 100 19.511 54.953 1.08% -1.175 -5.68% ⚠️
Complex Combinations & Edge Cases High Frequency test Creation 176.53 8.8443 2.93% -10.66 -5.69% ⚠️
Core Test Functionality test (High Volume, Same Name) 4.224 242.33 1.05% 0 0.00%
Core Test Functionality test (High Volume, Unique Names) 4.235 256.32 2.17% 0 0.00%
Raw Output
See CI logs for full output

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant