Align fluent and attribute skip semantics - #3571
Conversation
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
c132a28 to
17dd8d2
Compare
|
Rebased onto current Validation after rebase:
@codex review |
There was a problem hiding this comment.
Review: Align fluent and attribute skip semantics (#3571)
Solid, well-tested change. The OR-to-skip default for repeated WithSkipWhen (matching [SkipIf<T>]) plus the explicit WithSkipWhenAll(...) opt-in for AND semantics is the right call from the issue's two proposed fixes — it keeps the common case (SkipIf<A> + SkipIf<B> migrated to fluent) behaving the same way instead of silently inverting to AND. The ComposeSkipConditions/ComposeAllSkipConditions split (outer OR across registered groups, inner AND within a WithSkipWhenAll group) is clean and the short-circuit tests (WithSkipWhen_RepeatedCalls_OrComposeAndShortCircuit, WithSkipWhenAll_StopsWhenConditionDoesNotSkip) cover the important edge cases.
Worth calling out explicitly (not a bug, but a hidden consequence of the unification)
Moving SkipIf/RunIfAll/RunIfAny evaluation out of discovery and into ModuleExecutionPipeline (after dependency waiting) has a side effect on validation that isn't mentioned in the summary or docs:
ModuleRetriever.DiscoverModulesnow calls_moduleConditionHandler.ShouldIgnoreByCategory(category-only) instead ofShouldIgnore(category and attribute conditions).GetRunnableModulesForValidationuses the same discovery path, soPipelineValidator's fluent-dependency check now runs against every module, including ones an attribute condition provably skips.- Before this PR, a module like
[OnWindows] [DependsOn-via-fluent<WindowsOnlyModule>]that would never run on Linux was excluded from discovery entirely (cascaded intoIgnoredModules), so a missing fluent dependency never surfaced as a validation error. After this PR, that same module now throwsPipelineValidationExceptionatBuildAsync/ValidateAsynctime regardless of platform, because attribute-gated modules are no longer treated as statically ignorable for validation purposes. - The test suite tracks this correctly (
BuildAsync_Rejects_Fluent_Missing_Dependency_For_Execution_Skipped_Module/ValidateAsync_Rejects_Fluent_Missing_Dependency_For_Execution_Skipped_Modulereplace the old..._Ignores_Fluent_Dependencies_For_Discovery_Skipped_Modulestests), so this isn't an oversight — but it's a real behavior change for anyone who used attribute conditions as a way to guard fluentDependsOn<T>()chains that only exist on some platforms/environments. Given the issue is already flaggedBreaking: yes, I'd suggest adding a line to the PR description/changelog (and maybe a doc note inrun-conditions.mdorskipping.md) explicitly stating that fluent dependencies declared on attribute-conditioned modules are now always validated, independent of whether the condition will skip the module at runtime. Silent breaking changes in validation strictness are the kind of thing that turn into confusing GitHub issues down the line ("this pipeline built fine last version, now it throws on CI").
Arguably this is a correctness improvement (an IRunCondition can depend on runtime/HTTP/env state, so "provably skipped at discovery" was already a fragile guarantee to build validation leniency on), but it should be a documented, intentional decision rather than an emergent side effect of the pipeline unification.
Minor
ModuleExecutionPipeline.ExecuteAsync's fallbackSkipDecision.Skip("Module was ignored")duplicates the same fallback string inModuleRetriever.DiscoverModules. Not worth a shared helper for one string, just noting the duplication.ModuleConditionHandler.EvaluateShouldIgnorere-evaluatesEvaluateCategoryConditionsfor every module reachingModuleExecutionPipeline, even thoughModuleRetrieveralready ranShouldIgnoreByCategoryfor the same module during discovery. Category conditions are cheap/pure so this is harmless, but it is a redundant second evaluation per module now that the two check-paths are separate — worth a one-line comment if intentional, since a future reader may assumeShouldIgnoreandShouldIgnoreByCategoryare mutually exclusive call sites.
Everything else (the WithSkipWhenAll null/empty validation, the lifecycle/hook-invocation test additions in LifecycleEventIntegrationTests and DirectModuleHooksTests, and the doc updates) looks accurate and consistent with the stated goal of unifying skip hook/notification behavior between attributes and the fluent API.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17dd8d211f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
17dd8d2 to
da5bf7d
Compare
Follow-up reviewI reviewed this PR previously. The diff is still patch-identical to that review ( One item from the earlier review is still outstanding: Why this is worth documenting explicitly rather than leaving as an emergent side effect: anyone currently relying on attribute conditions to guard platform/environment-specific fluent No other issues found; the rest of the change (the |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Addressed review feedback: async WithSkipWhenAll arrays are snapshotted, with regression coverage; unconditional fluent-dependency validation is documented in the guide and PR description. Configuration tests pass (33/33), validation tests pass (31/31), core Release build has 0 errors. Branch updated from main. @codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1c54ec6163
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Follow-up review (post-rebase, head f735223)
I reviewed this PR twice before; this pass verifies the current head against those earlier rounds rather than re-deriving everything from scratch.
Both previously-raised items are now resolved:
- Async
WithSkipWhenAllsnapshot (Codex,ModuleConfigurationBuilder.cs): the async overload now doesComposeAllSkipConditions([.. conditions]), which spreads into a fresh array before storing it — mutating the caller's original array afterBuild()no longer changes composed skip behavior. Matches the sync overload'sArray.ConvertAllsnapshot. - Undocumented validation-strictness change (my earlier review):
run-conditions.mdnow has an explicit paragraph — "Fluent dependencies are validated before execution conditions are evaluated. Every dependency declared withDependsOn<T>()must therefore be registered, even when an attribute condition will skip the consuming module..." — and the PR description carries the same note under "Validation behavior." That closes the gap between "tests prove this is intentional" and "a consumer reading the docs would know to expect it."
Checked the post-merge state of ModuleRetriever.cs specifically, since the merge from main touched that file (conflict noted in the merge commit) — DiscoverModules/GetRunnableModulesForValidation still correctly call ShouldIgnoreByCategory (not ShouldIgnore), so the validation-strictness behavior survived the rebase onto #3569 ("Harden category filter matching and validation") cleanly.
Design read
The ComposeSkipConditions (OR across groups, stops on first skip) / ComposeAllSkipConditions (AND within a WithSkipWhenAll group, short-circuits on first non-skip) split is the right shape: it keeps WithSkipWhen's default behavior aligned with repeated [SkipIf<T>] attributes (OR-to-skip) rather than silently flipping to AND, while still giving callers who want AND semantics an explicit, discoverable API (WithSkipWhenAll) instead of forcing them to hand-roll a combinator inside a single lambda. Routing attribute conditions through ModuleExecutionPipeline (after dependency waiting) instead of evaluating them only at discovery is the correct fix for the underlying bug — it's what makes attribute-driven skips invoke the same OnSkippedAsync/lifecycle notifications as fluent skips, which is the whole point of the issue this closes.
Still-minor, not blocking
ModuleConditionHandler.EvaluateShouldIgnore(called fromModuleExecutionPipeline) re-runsEvaluateCategoryConditionsper module even thoughModuleRetrieveralready calledShouldIgnoreByCategoryfor the same module during discovery. Category conditions are cheap and pure, so this is harmless today, but the two now-separate call sites (ShouldIgnoreByCategoryfor discovery,ShouldIgnorefor execution) aren't obviously non-overlapping to a future reader — a one-line comment onShouldIgnorenoting it deliberately re-checks category conditions would save someone a "wait, is this a bug?" moment later.- The
SkipDecision.Skip("Module was ignored")fallback string is now duplicated betweenModuleRetriever.DiscoverModulesandModuleExecutionPipeline.ExecuteAsync. Not worth a shared constant for one string, just flagging it stayed as-is.
Nothing else to add — the OR/AND composition tests, the LifecycleEventIntegrationTests/DirectModuleHooksTests additions covering attribute-triggered OnSkippedAsync/event-log invocation, and the validation test renames (..._Rejects_Fluent_Missing_Dependency_For_Execution_Skipped_Module) all line up with the stated goal and confirm the behavior is intentional rather than emergent. Approving from a code standpoint.
|
Fixed distributed discovery: master-side category filtering now also rejects impossible OS intersections, while a single routable OS target remains deferred to workers. Condition handler: 11/11; OS mapping: 3/3. @codex review |
Follow-up reviewReviewed the latest changes since the previous review (head now Previously flagged issue is resolved. The unconditional fluent-dependency-validation behavior change is now called out explicitly in the PR description ("Validation behavior" section) and in New in this round — distributed OS-routing fix:
Architecture check on the discovery/execution split: No new issues found. The composition of |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Follow-up review (head
|
Follow-up review (head
|
Make repeated WithSkipWhen calls OR-to-skip like SkipIf attributes. Add explicit WithSkipWhenAll groups and route attribute conditions through the execution lifecycle so all skipped hooks and notifications fire consistently. Fixes #3487.
284705f to
476f733
Compare
Follow-up review (head
|
Summary
WithSkipWhencalls compose OR-to-skip, matching repeated[SkipIf<T>]attributesWithSkipWhenAll(params ...)groups for AND-to-skip behaviorValidation
ModularPipelines.slnRelease build passed with 0 errors (existing warnings only)git diff --checkpassedFixes #3487
Validation behavior
Fluent dependencies are validated before execution conditions. Attribute-conditioned modules must therefore register every
DependsOn<T>()dependency even when the condition will skip them on the current platform or environment.Distributed OS-routing follow-up
ModuleConditionHandlerTests: 11/11;OperatingSystemConditionsTests: 3/3.