Web pipeline: step modes + audit artifacts on fail#85
Conversation
Pull Request Review: Web pipeline step modes + audit artifacts on failSummaryThis PR adds step-level mode gating functionality to the web pipeline, allowing users to conditionally execute steps based on the pipeline mode (e.g., Code Quality & Best PracticesStrengths:
Minor Issues:
Potential Bugs or Issues
Performance Considerations
Security ConcernsNo security issues identified. The code:
Test CoverageCritical Gap: No unit tests were added for the new mode gating functionality. Recommended tests:
Example test structure: [Fact]
public void ShouldExecuteStepMode_WhenModeMatchesSingleMode_ReturnsTrue()
{
// Arrange: step with mode="dev", effectiveMode="dev"
// Act: call ShouldExecuteStepMode
// Assert: returns true
}DocumentationThe documentation in
Suggestion: Add a note about combining Overall AssessmentThis is a well-implemented feature that adds useful functionality with minimal complexity. The main concern is the lack of unit tests for the new mode gating logic. Given that this affects pipeline execution flow, comprehensive tests are essential. Recommendations
The code is production-ready pending test coverage. Once tests are added, this would be a solid improvement to the pipeline system. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bbe3f4fa03
ℹ️ 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".
| var skipReason = GetSkipReason(task, step, effectiveMode, onlyTaskSet, skipTaskSet); | ||
| if (skipReason is not null) |
There was a problem hiding this comment.
Treat mode-skipped dependencies as not executed
When a step is filtered out by mode/modes/skipModes, GetSkipReason now causes it to be marked as a successful skip, but dependency enforcement later only checks dependencyResult.Success; this means downstream steps with dependsOn can still run even though their prerequisite never executed. In pipelines where a producer step is mode-gated (for example build in ci) and a consumer step is not, running in another mode can execute the consumer against stale or missing artifacts, which is a correctness regression introduced by the new mode filtering path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds pipeline step-level mode gating and adjusts audit artifact writing so summary/SARIF can be emitted only on failure (consistent with doctor), with corresponding schema + documentation updates.
Changes:
- Add per-step mode filtering via
mode/modes/onlyModes(+ kebab-case aliases) andskipModes(+ alias), withdefaultas the effective mode when--modeis omitted. - Update pipeline audit/doctor execution to honor
SummaryOnFailOnly/SarifOnFailOnlybehavior when*OnFailis enabled butsummary/sarifisn’t explicitly enabled. - Document step modes and extend the pipeline spec JSON schema with the new mode-related fields.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
schemas/powerforge.web.pipelinespec.schema.json |
Adds mode-related fields to step definitions (schema support for step mode gating). |
PowerForge.Web.Cli/WebPipelineRunner.cs |
Implements effective mode defaulting + step skip reasoning by mode; aligns audit artifact on-fail handling. |
Docs/PowerForge.Web.Pipeline.md |
Documents --mode semantics and the new per-step mode gating fields with an example. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var modes = GetArrayOfStrings(step, "modes") ?? | ||
| GetArrayOfStrings(step, "onlyModes") ?? GetArrayOfStrings(step, "only-modes"); | ||
| if (!string.IsNullOrWhiteSpace(mode)) | ||
| modes = modes is null ? new[] { mode } : modes.Concat(new[] { mode }).ToArray(); | ||
|
|
||
| var skipModes = GetArrayOfStrings(step, "skipModes") ?? GetArrayOfStrings(step, "skip-modes"); |
There was a problem hiding this comment.
ShouldExecuteStepMode treats modes/onlyModes/only-modes as mutually exclusive by using ?? (first non-null wins). If a pipeline step accidentally specifies more than one of these arrays, the later ones are silently ignored, which can lead to unexpected step execution/skipping. Consider either merging all provided arrays into a single allow-list, or explicitly detecting the conflict and failing with a clear error message (same applies to skipModes vs skip-modes).
| var modes = GetArrayOfStrings(step, "modes") ?? | |
| GetArrayOfStrings(step, "onlyModes") ?? GetArrayOfStrings(step, "only-modes"); | |
| if (!string.IsNullOrWhiteSpace(mode)) | |
| modes = modes is null ? new[] { mode } : modes.Concat(new[] { mode }).ToArray(); | |
| var skipModes = GetArrayOfStrings(step, "skipModes") ?? GetArrayOfStrings(step, "skip-modes"); | |
| // Combine all possible "allowed modes" sources into a single array. | |
| List<string>? modesList = null; | |
| var modesFromModes = GetArrayOfStrings(step, "modes"); | |
| if (modesFromModes is not null && modesFromModes.Length > 0) | |
| { | |
| modesList ??= new List<string>(); | |
| modesList.AddRange(modesFromModes); | |
| } | |
| var modesFromOnlyModes = GetArrayOfStrings(step, "onlyModes"); | |
| if (modesFromOnlyModes is not null && modesFromOnlyModes.Length > 0) | |
| { | |
| modesList ??= new List<string>(); | |
| modesList.AddRange(modesFromOnlyModes); | |
| } | |
| var modesFromOnlyModesKebab = GetArrayOfStrings(step, "only-modes"); | |
| if (modesFromOnlyModesKebab is not null && modesFromOnlyModesKebab.Length > 0) | |
| { | |
| modesList ??= new List<string>(); | |
| modesList.AddRange(modesFromOnlyModesKebab); | |
| } | |
| if (!string.IsNullOrWhiteSpace(mode)) | |
| { | |
| modesList ??= new List<string>(); | |
| modesList.Add(mode); | |
| } | |
| var modes = modesList is not null && modesList.Count > 0 ? modesList.ToArray() : null; | |
| // Combine all possible "skip modes" sources into a single array. | |
| List<string>? skipModesList = null; | |
| var skipModesCamel = GetArrayOfStrings(step, "skipModes"); | |
| if (skipModesCamel is not null && skipModesCamel.Length > 0) | |
| { | |
| skipModesList ??= new List<string>(); | |
| skipModesList.AddRange(skipModesCamel); | |
| } | |
| var skipModesKebab = GetArrayOfStrings(step, "skip-modes"); | |
| if (skipModesKebab is not null && skipModesKebab.Length > 0) | |
| { | |
| skipModesList ??= new List<string>(); | |
| skipModesList.AddRange(skipModesKebab); | |
| } | |
| var skipModes = skipModesList is not null && skipModesList.Count > 0 ? skipModesList.ToArray() : null; |
| "mode": { "type": "string" }, | ||
| "modes": { "type": "array", "items": { "type": "string" } }, | ||
| "onlyModes": { "type": "array", "items": { "type": "string" } }, | ||
| "only-modes": { "type": "array", "items": { "type": "string" } }, | ||
| "skipModes": { "type": "array", "items": { "type": "string" } }, | ||
| "skip-modes": { "type": "array", "items": { "type": "string" } }, |
There was a problem hiding this comment.
The pipeline runner parses summaryOnFail/summary-on-fail and sarifOnFail/sarif-on-fail for audit/doctor steps, but those fields are not present in the JSON schema. This makes valid configs fail schema validation and also hides the new/updated on-fail artifact behavior from schema-driven tooling. Please add these properties (and their kebab-case aliases) to the relevant step definitions in the schema.
Adds step-level mode gating via mode/modes/skipModes (effective mode defaults to default) and aligns audit summary/SARIF *OnFail behavior with doctor. Also updates docs and schema.