Add PowerForge.Web agent readiness support#318
Conversation
Code Review — PR #318: Add PowerForge.Web agent readiness supportOverviewThis PR adds a comprehensive The feature is well-scoped and clearly documented. The main concerns below are roughly ordered by severity. Issues & Suggestions1. Duplicate constants — dead alias (minor bug)// WebAgentReadiness.cs
private const string AgentBlockStart = "# BEGIN PowerForge Agent Readiness";
private const string AgentBlockEnd = "# END PowerForge Agent Readiness";
private const string HeadersBlockStart = "# BEGIN PowerForge Agent Readiness"; // identical to AgentBlockStart
private const string HeadersBlockEnd = "# END PowerForge Agent Readiness"; // identical to AgentBlockEnd
2.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a4c5e7e97
ℹ️ 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 apiCatalogPath = Path.Combine(siteRoot, ".well-known", "api-catalog"); | ||
| AddCheck(checks, "api-catalog", "api-auth-mcp-skill-discovery", "API Catalog (RFC 9727)", | ||
| ValidateApiCatalog(apiCatalogPath, out var apiCatalogMessage) ? "pass" : "fail", |
There was a problem hiding this comment.
Honor disabled discovery checks in verify mode
This check is unconditional even when agentReadiness.apiCatalog.enabled is set to false, so Verify() (and therefore Prepare(), which calls Verify()) can report Success=false for configurations that explicitly opt out of API catalog generation. The same pattern appears for other optional checks in this method, which makes feature flags ineffective and can fail CI when failOnFailures is enabled despite a valid config.
Useful? React with 👍 / 👎.
| if (!string.IsNullOrWhiteSpace(apiCatalogPath)) | ||
| { | ||
| written.Add(apiCatalogPath!); | ||
| linkTargets.Add(new HeaderLinkTarget("/.well-known/api-catalog", "api-catalog", "application/linkset+json")); |
There was a problem hiding this comment.
Use configured API catalog path in discovery link headers
The API catalog file path is configurable via AgentApiCatalogSpec.OutputPath, but the generated link target is hard-coded to /.well-known/api-catalog. When a site uses a custom output path, headers (and downstream checks/scanners that follow them) point to the wrong URL, so readiness signals become inconsistent and can be reported as broken even though the file was generated.
Useful? React with 👍 / 👎.
Code Review: PowerForge.Web Agent Readiness EngineOverviewThis PR adds a substantial new feature (~2,800 lines net) that enables static sites to prepare, verify, and scan AI/agent-readiness signals including robots.txt Content Signals, security headers, API catalog, Agent Skills, agents.json, A2A Agent Card, MCP Server Card, and structured data checks. The feature is split cleanly into models, a service class, CLI handlers, a pipeline task, JSON schema updates, and docs. Overall the code is well-structured and includes good defensive coding. Correctness / Bugs
// WebAgentReadiness.cs ~line 1843
if (spec.Enabled)
{
spec.SecurityHeaders ??= new AgentSecurityHeadersSpec();
...
}
return spec;This silently modifies the
AddCheck(checks, "sitemap", "discoverability", "sitemap.xml",
ValidateSitemap(sitemapPath, out var sitemapMessage) ? "pass" : "fail", ...)Unlike every other check in the method, there is no Code Quality
private static bool HasRobotsUserAgent(string text)
=> text.Contains("User-agent:", StringComparison.OrdinalIgnoreCase) ||
text.Contains("User-Agent:", StringComparison.OrdinalIgnoreCase);
Inline Regex patterns are not cached Synchronous wait on async in CLI and pipeline // WebCliCommandHandlers.AgentReady.cs
result = WebAgentReadiness.ScanAsync(...).GetAwaiter().GetResult();This is a standard pattern for console apps and is fine here, but it's worth noting that if the surrounding host ever becomes async-friendly, these call sites should be updated. Performance
Directory.EnumerateFiles(siteRoot, "*.html", SearchOption.AllDirectories)
.Take(500)
.Any(file => File.ReadAllText(file).Contains("navigator.modelContext.provideContext", ...));This can read a large amount of data for big sites. Since
SecurityDefault CSP includes This is pragmatic for documentation sites that use inline scripts and styles, and users can override it. The documentation and the doc comment clearly note it can be customized. The current default is fine as a starting point, but consider adding a note in the docs about tightening this for sites that don't need Path traversal protection is solid CORS wildcard on well-known discovery resources Test CoverageThe four tests cover the key happy paths and one security path well:
Missing coverage worth considering:
Dependency UpdateBumping SummaryThis is a large, well-executed feature with clean architecture, solid defensive coding, and useful documentation. The three most actionable items before merging:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b08cecc26
ℹ️ 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 spec = ResolveSpec(options.AgentReadiness); | ||
| var checks = new List<WebAgentReadinessCheck>(); | ||
| var warnings = new List<string>(); | ||
|
|
||
| var robotsPath = Path.Combine(siteRoot, "robots.txt"); |
There was a problem hiding this comment.
Short-circuit verify when agent readiness is disabled
Verify() resolves the spec and immediately runs all checks without honoring AgentReadinessSpec.Enabled == false, so agent-ready verify can fail on missing robots/headers/sitemap even when the feature is explicitly turned off in site.json. This makes a valid opt-out configuration fail CI for verify-mode workflows.
Useful? React with 👍 / 👎.
| if (spec.LinkHeaders) | ||
| written.Add(UpdateHeaders(siteRoot, spec, linkTargets)); |
There was a problem hiding this comment.
Generate security headers independently of Link header toggle
Header generation is gated entirely by spec.LinkHeaders, so setting linkHeaders: false prevents writing _headers at all, including security/CORS directives controlled by securityHeaders. In that configuration (with security defaults still enabled), Prepare() produces no security headers and then Verify() reports failures, so a valid “no Link hints” setup becomes self-failing.
Useful? React with 👍 / 👎.
| sb.AppendLine("/.well-known/api-catalog"); | ||
| sb.AppendLine(" Content-Type: application/linkset+json; profile=\"https://www.rfc-editor.org/info/rfc9727\""); | ||
| AppendCorsHeaders(sb, security); |
There was a problem hiding this comment.
Use configured API catalog route in
_headers resource block
The _headers block hard-codes /.well-known/api-catalog for content-type/CORS rules instead of using apiCatalog.outputPath. When a custom catalog path is configured, the generated catalog file won’t receive the intended static header rules while a non-existent default route does, leading to inconsistent discovery behavior (especially MIME/CORS checks) despite successful file generation.
Useful? React with 👍 / 👎.
Code Review: Add PowerForge.Web Agent Readiness SupportOverviewThis PR adds a comprehensive The code is well-structured and the feature scope is clearly defined. Below are observations grouped by concern. Security
CSP default in docs is permissive — The example Input values written verbatim into Correctness / Potential Bugs
Code Quality
Dependency / Package ChangesUpgrading Test CoverageThe seven new xUnit tests cover the main happy paths and important edge cases well. Notable gaps worth tracking as follow-up:
Documentation
SummarySolid feature addition with good security hygiene (path-traversal guard, spec immutability design, NU1903 override). Three items to address before merging:
Everything else noted above is advisory / low priority. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a7f0f5fd32
ℹ️ 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".
| { | ||
| private static void ExecuteAgentReady(JsonElement step, string baseDir, string lastBuildOutPath, WebPipelineStepResult stepResult) | ||
| { | ||
| var operation = GetString(step, "operation") ?? GetString(step, "mode") ?? "prepare"; |
There was a problem hiding this comment.
Stop using pipeline mode as agent-ready operation
mode is already used by pipeline scheduling (ShouldExecuteStepMode) to mean execution mode constraints, but this fallback also interprets it as the agent-ready operation. For a step like { "task": "agent-ready", "mode": "ci" } (with no explicit operation), operation becomes ci, the switch falls into the default case, and the step throws instead of running the default prepare behavior.
Useful? React with 👍 / 👎.
| if (string.IsNullOrWhiteSpace(siteRoot)) | ||
| return Array.Empty<string>(); |
There was a problem hiding this comment.
Treat implicit build output as cacheable agent-ready output root
Returning no expected outputs when siteRoot is omitted makes cache validation too weak for this task: ExecuteAgentReady can still run using lastBuildOutPath, but cache checks consider an empty output list as present. In that configuration, if generated agent-ready artifacts are removed between runs, the step can be incorrectly skipped as a cache hit and leave required files missing.
Useful? React with 👍 / 👎.
Code Review: PR #318 — PowerForge.Web Agent Readiness SupportOverviewThis is a substantial, well-scoped feature that adds a three-operation engine ( Strengths
Issues1.
|
| Scenario | Covered |
|---|---|
Prepare writes all expected files |
✅ |
Prepare with custom OutputPath |
✅ |
Prepare rejects path traversal |
✅ |
Prepare with link headers disabled |
✅ |
Verify disabled short-circuit |
✅ |
Verify disabled optional checks (no failures) |
✅ |
ResolveSpec non-mutation |
✅ |
Pipeline integration (agent-ready after build+sitemap) |
✅ |
| BotRules newline injection | ❌ |
Remote scan (live HTTP) |
❌ (expected — HTTP mocking would help) |
| Large response body / OOM guard | ❌ |
Summary
The feature is well-designed and ready for use. The robots.txt injection issue (#1) is the only concern worth addressing before merging — the rest are robustness improvements or post-merge housekeeping. Excellent work on the path traversal guards and spec immutability.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b29cb062f
ℹ️ 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".
| if (string.IsNullOrWhiteSpace(siteRoot)) | ||
| return Array.Empty<string>(); |
There was a problem hiding this comment.
Disable cache hits for agent-ready scan steps
For agent-ready steps that run operation: "scan", pipelines typically provide only url and no siteRoot; this branch then returns an empty expected-output list. In RunPipeline, an empty output list is treated as present, so a prior successful scan can be replayed as a cache hit and the live HTTP checks are skipped on later runs, allowing remote regressions to pass unnoticed. scan should be excluded from caching (or use a remote-aware invalidation signal).
Useful? React with 👍 / 👎.
| Path.Combine(siteRoot, "robots.txt"), | ||
| Path.Combine(siteRoot, ".well-known", "api-catalog"), | ||
| Path.Combine(siteRoot, ".well-known", "agent-skills", "index.json"), | ||
| Path.Combine(siteRoot, ".well-known", "mcp", "server-card.json"), | ||
| Path.Combine(siteRoot, headersPath) |
There was a problem hiding this comment.
Stop requiring optional agent-ready artifacts for cache reuse
The cache output list for agent-ready is hard-coded to include artifacts like /.well-known/mcp/server-card.json, even though MCP is optional and disabled by default; as a result, AreExpectedOutputsPresent will fail on normal configurations and the step will never get a cache hit. This also ignores configured custom output paths, so valid runs are treated as cache misses indefinitely.
Useful? React with 👍 / 👎.
Code Review — PR 318: Add PowerForge.Web agent readiness supportOverviewThis PR adds a comprehensive agent-ready pipeline task and CLI surface that generates, verifies, and scans agent-facing discovery assets (robots.txt signals, headers, agents.json, A2A card, MCP card, API catalog, Agent Skills index, OpenAPI detection). It also bumps System.Security.Cryptography.Xml / Pkcs from 8.0.0 to 10.0.6 to clear NU1903. The design is solid and the feature coverage is excellent. A few issues and observations follow. SecurityPath-traversal guard is correct but SourcePath has a gap ResolveSitePath uses Path.GetFullPath + prefix check and is properly tested by Prepare_RejectsAgentOutputPathOutsideSiteRoot. Good. However, the skill.SourcePath read path calls Path.GetFullPath(skill.SourcePath.Trim().Trim('"')) but does NOT verify the resolved path sits inside the site root (or any other safe boundary). An operator who controls site.json could point sourcePath at "../../../../etc/passwd" and the file would be read and embedded into generated output. Consider applying the same ResolveSitePath guard or restricting to the config-file directory. HttpClient instantiated per ScanAsync call
Code Quality / DesignReflection in tests couples tests to implementation details GetExpectedStepOutputs and IsCacheableStep are private methods tested via reflection. A rename or signature change makes the tests silently no-ops — Assert.NotNull(method) only guards the null case, not a signature mismatch. Consider making these internal and exposing them via [InternalsVisibleTo] so the compiler validates call sites. --fail-on-warnings is silently aliased to --fail-on-failures All three handlers treat --fail-on-warnings identically to --fail-on-failures, but the flag is absent from the help text. Either document it with distinct semantics (exit non-zero when Warnings.Count > 0), or remove the alias before it becomes a hard-to-change undocumented contract. verify cacheability is unconfirmed Only prepare is explicitly cacheable; scan is excluded. But verify produces no output files tracked by the cache. If verify is run in a cached pipeline and the cache key is warm, it may be skipped incorrectly. A test parallel to AgentReadyScanStepsAreNotCacheable for the verify operation would make the intent explicit. Repeated null-coalescing for output paths There are ~8 identical patterns like: Dependency BumpThe 8.0.0 -> 10.0.6 bump for Cryptography.Xml/Pkcs (+ Microsoft.Bcl.Cryptography, System.Formats.Asn1) to clear NU1903 is correct. The minimum-floor pin [10.0.6, ) is the right syntax for a security override, and lock files are updated consistently across all four projects. One note: PowerForge.PowerShell.csproj gates the PackageReference on net8.0 Or net10.0. The project also targets net472 — confirm the net472 TFM does not resolve the old transitive version of Cryptography.Xml through a separate path. Test CoverageGood breadth: prepare, verify, disabled-path, no-mutate, pipeline integration, scan cacheability, optional-output honor, and path-traversal rejection are all covered. Gaps worth noting:
Minor Nits
SummaryThe implementation is well-structured, the path-traversal guard is correctly implemented, the documentation is thorough, and the test suite covers the main paths. The two items most worth addressing before merge are the SourcePath read boundary gap and the undocumented --fail-on-warnings alias. The reflection-in-tests concern is lower priority but worth tracking. |
Summary
Validation
Full solution test note: dotnet test .\PSPublishModule.sln -c Release --no-restore is no longer blocked by NU1903, but it hit pre-existing unrelated failures and then hung, so I stopped dotnet/vstest.