fix(audit): distinguish README comparison failure from content mismatch#282
Merged
sadlilas merged 2 commits intomicrosoft:mainfrom May 5, 2026
Conversation
The check-readme-sections step initialized CONTRIB_MATCH and TRADE_MATCH to
"false", then only flipped them to "true" on a successful diff comparison.
Any failure inside the extraction pipeline (empty reference, missing heading,
sed/grep error, partial fetch) silently left the flags at "false" and the
recipe reported "README.md sections exist but content differs from template"
even when the actual content was verbatim correct.
This produced a false positive on amplifier-collections in the 2026-05-04
Microsoft ecosystem audit. Re-running the recipe's exact extraction logic
against the same files produced TRADE_MATCH=true, confirming the content
was identical and the prior "false" was a comparison failure being
mis-reported as a content mismatch.
Changes:
- Three-state match flags ("true" / "false" / "unknown") replace the binary
default-false flags. "unknown" means "comparison could not be performed."
- Sanity-check the reference README before relying on it: must be non-empty
and must contain both ## Contributing and ## Trademarks headings,
otherwise flag REF_README_OK=false.
- Skip a comparison if either extracted file is empty (treat as "unknown"
rather than silently mismatching).
- Severity mapping now distinguishes:
* missing-section -> error (real finding, fix the README)
* unknown match -> warning, "comparison could not complete; retry
recommended" (transient; further differentiates "reference unavailable"
from "extraction failed" so the report points to the right remediation)
* false match -> warning, "content differs from template" (real content
mismatch, the original finding)
- The JSON contract still emits boolean *_verbatim fields, but they now
derive from the three-state flag (only "true" maps to true). The distinct
signal for "comparison failed" is carried by severity and finding text.
This is the smallest fix that addresses the user-visible bug. It does not
restructure the working directory layout (deferred until evidence shows
parallel writes are causing extraction failures) and does not change the
canonicalization logic (the existing whitespace-normalized awk diff is
sufficient when extraction succeeds).
Future audit runs that hit the same transient condition will now report
"comparison could not complete; retry recommended" instead of falsely
asserting that the README content differs from the template.
Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…repos The canonical Microsoft amplifier branch-protection ruleset is not applied to private repos in this org by policy. Auditing private repos against public-repo ruleset expectations produced 100% false-positive findings on every private repo in the 2026-05-04 audit (15/15), drowning out actual issues in the report. This commit makes the two ruleset-related steps short-circuit early when the target repo is private: - check-branch-protection: emits a "pass" verdict noting the skip - check-bypass-actors: emits a "pass" verdict noting the skip Implementation: - fetch-repo-info now requests the isPrivate field from gh repo view --json - Both check steps parse isPrivate from repo_info_raw and exit 0 with a "Skipped: private repo (canonical ruleset is not applied to private repos per policy)" finding when isPrivate=true - Severity "pass" ensures these no longer count as errors or warnings in the aggregator. The finding text explains the skip so reviewers reading per-repo output can see why no real check ran Scope deliberately limited to ruleset-related checks (branch protection and bypass actors). Other private-repo checks remain in scope: - Document compliance (LICENSE, SECURITY.md, SUPPORT.md, CODE_OF_CONDUCT.md, README sections) -- these are still expected to match templates - Feature-toggle checks (Wiki, Projects, Issues) -- not ruleset-related, governed by separate policy Net effect on a re-run: the 15/15 private-repo branch-protection findings disappear from the report, and remaining private-repo findings (Wiki, Projects, document drift) become visible without ruleset noise. Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
During the Microsoft ecosystem audit run on 2026-05-04, the
check-readme-sectionsstep inrecipes/repo-audit.yamlreported a false positive onmicrosoft/amplifier-collections:Re-running the recipe's extraction logic against the same files and reference template produced
TRADE_MATCH=true, confirming the content matched perfectly and the "false" result was a comparison failure being mis-reported as a content mismatch.Root Cause
The match flags in
check-readme-sectionsuse a binary false-default pattern:This collapses three distinct conditions into one outcome:
Conditions 3 and 4 both produce TRADE_MATCH=false and surface to the user as:
But condition 4 is a transient failure requiring retry, while condition 3 is a real content mismatch requiring a README edit. Users cannot distinguish the two from the current finding text.
The Fix
Three-state match flags replace binary defaults:
true= comparison completed, content matchedfalse= comparison completed, content differedunknown= comparison could not be completed (missing/empty files, extraction failure, sanity checks failed)Sanity-check the reference README before comparing:
## Contributingand## TrademarksheadingsREF_README_OK=falseif not; skip comparisons that rely on itSkip comparisons when extraction yields empty content:
unknownrather than silently failing the diffDistinct findings per condition:
ERRORseverity, "section not found" (user action required: add section)WARNINGseverity, "comparison could not complete; reference unavailable or extraction failed; retry recommended" (transient, requires retry)WARNINGseverity, "content differs from template" (real mismatch, user action required: update section)The JSON contract still emits boolean
*_verbatimfields (for machine consumers), but they now derive correctly from the three-state flag: onlytruemaps to true; bothfalseandunknownmap to false. The distinct signal for "comparison failed" is now carried by severity and finding text, allowing users and downstream tooling to respond appropriately.Scope Deliberately Limited
Not included (deferred pending evidence):
Per-repo reference directories: I hypothesized a race condition in the shared
reference/directory (one dir written by all parallel repo-audits in step 3). Investigation showed this is plausible but not proven — one false positive over 112 repos is consistent with a transient blip, not a structural race. Restructuring the working directory is speculative. Deferring until the same symptom reproduces or multiple repos show false positives.Canonicalization logic changes: The current whitespace-normalized awk diff (
awk '...' | sort | diff) is sufficient when extraction succeeds. No change needed.Verification
python3 -c "import yaml; yaml.safe_load(...)"bash -non extracted command blocksrecipes/repo-audit.yamlGenerated with Amplifier