Fast mode: scope optimize/audit to updated pages#79
Conversation
PR Review: Fast mode incremental optimize/auditSummaryThis PR implements an intelligent optimization for the Code Quality ✅Strengths:
Code style:
Potential Issues & Concerns 🔍1. AsyncLocal cleanup timing (Minor)PowerForge.Web/Services/WebSiteBuilder.cs:67-71 var prevSink = UpdatedSink.Value;
UpdatedSink.Value = MarkUpdated;
try { ... }
finally { UpdatedSink.Value = prevSink; }The 2. Case sensitivity edge case (Low)PowerForge.Web/Services/WebSiteBuilder.cs:18-20 private static readonly StringComparison FileSystemPathComparison =
OperatingSystem.IsWindows() || OperatingSystem.IsMacOS()
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;This is correct, but note that macOS has case-insensitive filesystems by default (APFS is configurable). The current implementation is pragmatic and matches platform defaults. 3. Path normalization consistency (Low)PowerForge.Web/Services/WebSiteBuilder.cs:309-312
4. Duplicate filtering in HashSet (Minor optimization)PowerForge.Web/Services/WebSiteBuilder.cs:51 var updated = new HashSet<string>(StringComparer.OrdinalIgnoreCase);Good use of HashSet for deduplication. The final Performance Considerations ✅Benefits:
Observations:
Security Concerns ✅No security issues identified:
Test Coverage ✅PowerForge.Tests/WebSiteBuilderIncrementalBuildTests.cs:80,94 Assert.Contains(build1.UpdatedFiles, p => p.Equals("index.html", ...));
Assert.Empty(build2.UpdatedFiles);Current coverage is good:
Suggested additional tests:
Additional ObservationsDocumentationThe PR body provides clear explanation of the feature, but consider adding inline comments for:
Edge cases handled well:
Recommendations
Verdict: ✅ Approve with minor suggestionsThis is a well-implemented performance optimization that follows established patterns in the codebase. The code quality is high, with proper error handling, thread safety, and test coverage. The feature provides clear value for iterative development workflows. The concerns raised are minor and mostly related to documentation and additional test coverage rather than correctness issues. The implementation is production-ready. Review generated with attention to security, performance, and maintainability |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8711bb0fe9
ℹ️ 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".
| .ToArray(); | ||
| if (updatedHtml.Length > 0) | ||
| { | ||
| include = string.Join(";", updatedHtml); |
There was a problem hiding this comment.
Use comma-delimited include list for fast audit scope
When fast mode auto-scopes audit, this line builds include with semicolons (";"), but pipeline include parsing only splits on commas (CliPatternHelper.SplitPatterns in PowerForge.Web.Cli/CliPatternHelper.cs:10). With 2+ updated HTML files and no explicit include, the audit receives a single pattern like "a.html;b.html", which matches no files, so checks are effectively skipped and can falsely pass with only the "No HTML files found" warning.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds incremental “fast mode” behavior by tracking which output files were updated during the last build, then using that list to scope subsequent optimize/audit steps to only the updated HTML pages—reducing iteration time on large sites.
Changes:
- Track per-build updated output files in
WebSiteBuilderand return them viaWebBuildResult.UpdatedFiles. - In CLI pipeline
--fastmode, auto-scope optimize/audit HTML includes to the updated pages from the most recent build step (when the step doesn’t already specify include patterns). - Extend incremental build tests to assert
UpdatedFilesbehavior across consecutive builds.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| PowerForge.Web/Services/WebSiteBuilder.cs | Introduces updated-file tracking via an AsyncLocal sink and returns UpdatedFiles in the build result. |
| PowerForge.Web/Models/WebSitePlan.cs | Extends WebBuildResult model to include UpdatedFiles. |
| PowerForge.Web.Cli/WebPipelineRunner.cs | Uses last build’s UpdatedFiles to scope optimize/audit when running with --fast. |
| PowerForge.Tests/WebSiteBuilderIncrementalBuildTests.cs | Updates test to validate UpdatedFiles is populated on first build and empty on unchanged rebuild. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| WriteAllTextIfChanged(planPath, JsonSerializer.Serialize(plan, jsonOptions)); | ||
| WriteAllTextIfChanged(specPath, JsonSerializer.Serialize(spec, jsonOptions)); | ||
| var updated = new HashSet<string>(StringComparer.OrdinalIgnoreCase); |
There was a problem hiding this comment.
updated is created with StringComparer.OrdinalIgnoreCase, which can collapse distinct paths on case-sensitive file systems (e.g., Linux) and produce an incorrect UpdatedFiles list. Use an OS-appropriate comparer (e.g., StringComparer.FromComparison(FileSystemPathComparison) or conditional Ordinal/OrdinalIgnoreCase) to match actual path semantics.
| var updated = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | |
| var updated = new HashSet<string>(StringComparer.FromComparison(FileSystemPathComparison)); |
| if ((htmlInclude is null || htmlInclude.Length == 0) && | ||
| lastBuildUpdatedFiles.Length > 0 && | ||
| string.Equals(Path.GetFullPath(siteRoot), lastBuildOutPath, FileSystemPathComparison)) |
There was a problem hiding this comment.
Fast-mode scoping compares Path.GetFullPath(siteRoot) to lastBuildOutPath without normalizing trailing directory separators. If one path ends with a separator and the other doesn’t, the equality check can fail and skip incremental scoping. Normalize both sides (e.g., trim trailing separators or use a shared helper) before comparing.
| if ((htmlInclude is null || htmlInclude.Length == 0) && | |
| lastBuildUpdatedFiles.Length > 0 && | |
| string.Equals(Path.GetFullPath(siteRoot), lastBuildOutPath, FileSystemPathComparison)) | |
| var siteRootFullPathNormalized = Path | |
| .GetFullPath(siteRoot) | |
| .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); | |
| var lastBuildOutPathNormalized = lastBuildOutPath? | |
| .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); | |
| if ((htmlInclude is null || htmlInclude.Length == 0) && | |
| lastBuildUpdatedFiles.Length > 0 && | |
| string.Equals(siteRootFullPathNormalized, lastBuildOutPathNormalized, FileSystemPathComparison)) |
| if (string.IsNullOrWhiteSpace(include) && | ||
| lastBuildUpdatedFiles.Length > 0 && | ||
| string.Equals(Path.GetFullPath(siteRoot), lastBuildOutPath, FileSystemPathComparison)) |
There was a problem hiding this comment.
Fast-mode scoping compares Path.GetFullPath(siteRoot) to lastBuildOutPath without normalizing trailing directory separators. If one path ends with a separator and the other doesn’t, the equality check can fail and skip incremental scoping. Normalize both sides (e.g., trim trailing separators or use a shared helper) before comparing.
| if (string.IsNullOrWhiteSpace(include) && | |
| lastBuildUpdatedFiles.Length > 0 && | |
| string.Equals(Path.GetFullPath(siteRoot), lastBuildOutPath, FileSystemPathComparison)) | |
| var normalizedSiteRoot = Path.GetFullPath(siteRoot) | |
| .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); | |
| var normalizedLastBuildOutPath = lastBuildOutPath? | |
| .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); | |
| if (string.IsNullOrWhiteSpace(include) && | |
| lastBuildUpdatedFiles.Length > 0 && | |
| string.Equals(normalizedSiteRoot, normalizedLastBuildOutPath, FileSystemPathComparison)) |
When running pipeline with --fast, automatically scope optimize/audit to the HTML pages updated by the last build step (unless the step already specifies include patterns). WebSiteBuilder now reports UpdatedFiles in WebBuildResult. This reduces optimize/audit time dramatically on large sites during local iteration.