incremental-build — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited incremental-build (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 7 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 7 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
For every diagnose or explain request, the final answer must explicitly cover all of these points, even when the immediate cause is simply missing Inputs/Outputs:
Do not edit unless the user asks for a fix. If a check cannot be run, label it as an unverified check rather than omitting it or claiming success. When Inputs or Outputs are absent, report timestamp comparison or one-to-one mapping as not applicable — the incremental mechanism is not engaged, not as an unverified comparison.
MSBuild's incremental build mechanism allows targets to be skipped when their outputs are already up to date, dramatically reducing build times on subsequent runs.
Inputs against all files listed in Outputs. If every output file is newer than every input file, the target is skipped entirely.<!-- This target is incremental: skipped if Output is newer than all Inputs -->
<Target Name="Transform"
Inputs="@(TransformFiles)"
Outputs="@(TransformFiles->'$(OutputPath)%(Filename).out')">
<!-- work here -->
</Target>
<!-- This target always runs because it has no Inputs/Outputs -->
<Target Name="PrintMessage">
<Message Text="This runs every build" />
</Target>Outputs, MSBuild doesn't know about them. The target may be skipped (because its declared outputs are up to date), but downstream targets may still be triggered.FileWrites item group won't be cleaned by dotnet clean. Over time, stale files can confuse incremental checks.@(Compile)) changes. Since these items feed into Inputs, the set of inputs changes and triggers a rebuild. This is expected behavior but can be surprising.Inputs or Outputs paths (e.g., $(Configuration), $(TargetFramework)) will cause rebuilds when changed. Switching between Debug and Release is a full rebuild by design.project.assets.json and potentially many resolved assembly paths. This changes the inputs to ResolveAssemblyReferences and CoreCompile, triggering a rebuild.VBCSCompiler) caches compilation state. If the server is recycled (timeout, crash, or manual kill), the next build may be slower even though MSBuild's incremental checks pass, because the compiler must repopulate its in-memory caches.Use binary logs (binlogs) to understand exactly why targets ran instead of being skipped.
Before recommending a fix, report this compact checklist for each target that reran:
Inputs includes every file that should invalidate the output, including volatile or generated dependencies. If a producer touches an input on every build, check whether its content changes. Fix unnecessary rewrites in the producer (for example, write only when content changes); if relevant content really changes, the dependent target must rerun. Exclude a file only with evidence that it cannot affect the output, never just because it changes often.For a diagnosis or explanation request, do not edit the project unless the user asks for a fix. If build execution is unavailable, distinguish inspected facts from expected behavior and do not claim the fix was applied or verified.
Overwrite="true" on WriteLinesToFile is not itself a reason that an incremental target reruns. The task rewrites the file only after MSBuild has already scheduled the target; correct Inputs/Outputs can skip the target before the task executes.
from the second build:
dotnet build /bl:first.binlog
dotnet build /bl:second.binlog -fl "-flp:v=diag;logfile=second-full.log;performancesummary"The first build establishes the baseline. The second build is the one you want to be incremental. Analyze second.binlog.
Use the binlog MCP server (Microsoft.AITools.BinlogMcp, exposed under the binlog MCP namespace) to analyze the second binlog:
load_binlog to load second.binlog.search_binlog for "Building target completely", "Building target incrementally", "Skipping target", and "is newer than output".get_target_info_by_name for the target that reran.get_project_target_list to confirm the target and its project context.get_expensive_targets only to rank the non-skipped targets after the skip reason is understood.another build or depend on replay support for the default fallback:
grep 'Building target\|Target.*was not skipped' second-full.logIn a perfectly incremental build, most targets should be skipped.
"Building target 'X' completely" — means MSBuild found no outputs or all outputs are missing; this is a full target execution."Building target 'X' incrementally" — means some (but not all) outputs are out of date."Skipping target 'X' because all output files are up-to-date" — target was correctly skipped. grep "is newer than output" second-full.logThis reveals exactly which input file's timestamp caused MSBuild to consider the target out of date.
first.binlog and second.binlog side by side in the MSBuild Structured Log Viewer to see what changed.grep 'Target Performance Summary' -A 30 second-full.log to see which targets consumed the most time in the second build — these are your optimization targets.The FileWrites item group is MSBuild's mechanism for tracking files generated during the build. It powers dotnet clean and helps maintain correct incremental behavior.
dotnet clean knows to remove them. Without this, generated files accumulate across builds and may confuse incremental checks.dotnet clean won't remove them, and they may cause stale data issues or confuse up-to-date checks.Add generated files to FileWrites inside the target that creates them:
<Target Name="MyGenerator" Inputs="..." Outputs="$(IntermediateOutputPath)generated.cs">
<!-- Generate the file -->
<WriteLinesToFile File="$(IntermediateOutputPath)generated.cs" Lines="@(GeneratedLines)" />
<!-- Register for clean -->
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)generated.cs" />
</ItemGroup>
</Target>Visual Studio has its own up-to-date check (Fast Up-to-Date Check, or FUTDC) that is separate from MSBuild's Inputs/Outputs mechanism. Understanding the difference is critical for diagnosing "it rebuilds in VS but not on the command line" issues.
<PropertyGroup>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup>CopyToOutputDirectory items that are newer than the last buildContent or None items with CopyToOutputDirectory="PreserveNewest" that have been modifiedThe following is a complete example of a well-structured incremental custom target:
<Target Name="GenerateConfig"
Inputs="$(MSBuildProjectFile);@(ConfigInput)"
Outputs="$(IntermediateOutputPath)config.generated.cs"
BeforeTargets="CoreCompile">
<!-- Generate file only if inputs changed -->
<WriteLinesToFile File="$(IntermediateOutputPath)config.generated.cs" Lines="..." />
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)config.generated.cs" />
<Compile Include="$(IntermediateOutputPath)config.generated.cs" />
</ItemGroup>
</Target>Key points in this example:
obj/ directory, which is managed by MSBuild and cleaned automatically.dotnet clean removes the generated file.<!-- BAD: No Inputs/Outputs — runs every build -->
<Target Name="BadTarget" BeforeTargets="CoreCompile">
<Exec Command="generate-code.exe" />
</Target>
<!-- BAD: Volatile output path — never finds previous output -->
<Target Name="BadTarget2"
Inputs="@(Compile)"
Outputs="$(OutputPath)gen_$([System.DateTime]::Now.Ticks).cs">
<Exec Command="generate-code.exe" />
</Target>
<!-- GOOD: Stable paths, registered outputs -->
<Target Name="GoodTarget"
Inputs="@(Compile)"
Outputs="$(IntermediateOutputPath)generated.cs"
BeforeTargets="CoreCompile">
<Exec Command="generate-code.exe -o $(IntermediateOutputPath)generated.cs" />
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)generated.cs" />
<Compile Include="$(IntermediateOutputPath)generated.cs" />
</ItemGroup>
</Target>MSBuild provides built-in tools to understand what's running and why.
dotnet build /clp:PerformanceSummaryThis shows a table of targets sorted by cumulative time, making it easy to spot targets that shouldn't be running in an incremental build.
dotnet msbuild /pp:preprocess.xmlSearch the preprocessed output to find where Inputs and Outputs are defined for any target, or to understand the full chain of imports.
PerformanceSummary) and what's imported (/pp), then cross-reference with binlog analysis for a complete picture.obj/ are tracked by MSBuild's clean infrastructure and won't leak between configurations.dotnet clean removes them and prevents stale file accumulation.Overwrite="true" WriteOnlyWhenDifferent="true" with WriteLinesToFile). Keep real dependencies in Inputs; if required volatile content changes, accept the dependent rebuild. Exclude a file only after proving it cannot affect the output.Outputs serves double duty: it defines the incremental check AND the items returned from the target. If you only need to pass items to calling targets without affecting incrementality, use Returns instead: <!-- Outputs: affects incremental check AND return value -->
<Target Name="GetFiles" Outputs="@(DiscoveredFiles)">...</Target>
<!-- Returns: only affects return value, no incremental check -->
<Target Name="GetFiles" Returns="@(DiscoveredFiles)">...</Target>~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.