Skip to content

M8a: quality & release gates (NFR-002/005/009 + CI/CD)#8

Merged
moisesja merged 3 commits into
mainfrom
feature/m8a-quality-release-gates
Jun 24, 2026
Merged

M8a: quality & release gates (NFR-002/005/009 + CI/CD)#8
moisesja merged 3 commits into
mainfrom
feature/m8a-quality-release-gates

Conversation

@moisesja

Copy link
Copy Markdown
Owner

First of three M8 PRs (M8 = conformance + interop + samples + gates). M8a lands the pure-.NET quality and release gates — no external runtime — closing the package / surface / doc halves of NFR-002, NFR-005, NFR-009 empirically and wiring CI/CD.

Verification: dotnet build -c Release is 0-warning under TreatWarningsAsErrors; 338 tests green (Core 145 + DI 154 + Architecture 9 + RoundTrip 9 + Rdfc 21; was 318).

What's in it

  • Credentials.TestSupport[FrTag] coverage attribute + RequirementIds (the §8 set: 34 FR + 9 NFR).
  • Credentials.ArchitectureTests (metadata-only via MetadataLoadContext):
    • no DataProofsDotnet type on the public surface (F3 / NFR-005) — return/param/field/property/generic-arg/base/interface vectors;
    • static no-Newtonsoft reference closure of Core + DI (NFR-002, fast pre-check);
    • empty-<summary> catcher (NFR-009) complementing CS1591-as-error;
    • net10 / async-signature gates (NFR-001/004);
    • FrCoverage gate — every §8 requirement has a [FrTag]-tagged test (NFR-007 deferred to M8c, logged).
  • Credentials.RoundTripTests — FR-003 byte-fidelity per issued family (verbatim received bytes; DI JCS+RDFC byte-stable, one proof; JOSE/SD-JWT verbatim compact + signed==source; COSE verbatim; H1 special-char fidelity).
  • Credentials.ConsumerProbe + tools/check-no-newtonsoft-closure.sh — authoritative package-level NFR-002 closure check (dotnet list --include-transitive).
  • Semver (NFR-005) — PublicAPI analyzers (RS0016/17) + committed PublicAPI.{Shipped,Unshipped}.txt (749/26/16); Microsoft.DotNet.ApiCompat.Tool + tools/check-api-compat.sh (skip-logged pre-release).
  • [FrTag] tagging across the suite + new FR-004 (lazy projection memoization) and FR-021 (status-list set/clear round-trip) tests.
  • CI/CD.github/workflows/ci.yml (ubuntu+windows matrix + no-newtonsoft + semver jobs) and release.yml (tag-driven pack + gate + push under a protected nuget-release environment).

Adversarial pass — two real findings fixed

Each gate was attacked to confirm it has teeth. Surface, semver (RS0016), doc (CS1591 + empty-summary), and FrCoverage (missing/unknown id) all failed correctly when defeated. Fixed:

  1. ConsumerProbe was hollow — re-packing the fixed 0.1.0 served stale same-version package metadata from NuGet's cache, so an injected Newtonsoft dependency was invisible (always reported the first run). Fixed via a unique per-run version delivered as an env-var MSBuild property (consistent across restore/build/dotnet list). Verified it now flags the violation.
  2. Static reference-closure blind spot — the compiler only records used references, so an unused-yet-declared package dep carrying Newtonsoft is invisible to the static walk. Documented; recursion broadened to all non-BCL assemblies; the ConsumerProbe is the authority. FrCoverage hardened to ignore commented-out tags.

Next: PR-B (samples + api-coverage), then PR-C (conformance + interop).

🤖 Generated with Claude Code

First of three M8 PRs (M8 = conformance + interop + samples + gates). Adds the
pure-.NET quality and release gates — no external runtime.

- Credentials.TestSupport: [FrTag] coverage attribute + RequirementIds (34 FR + 9 NFR).
- Credentials.ArchitectureTests: no-draft-type surface (F3/NFR-005), static no-Newtonsoft
  reference closure (NFR-002), empty-<summary> catcher (NFR-009), net10/async gates
  (NFR-001/004), and the FrCoverage gate (every PRD §8 requirement has a tagged test).
- Credentials.RoundTripTests: FR-003 byte fidelity per issued securing family.
- Credentials.ConsumerProbe + tools/check-no-newtonsoft-closure.sh: authoritative
  package-level NFR-002 transitive-closure check.
- PublicAPI analyzers (RS0016/17) + committed PublicAPI.{Shipped,Unshipped}.txt; ApiCompat
  tool (.config) + tools/check-api-compat.sh (skip-logged pre-release).
- [FrTag] tags across the suite + new FR-004 (lazy projection memoization) and FR-021
  (status-list set/clear round-trip) tests.
- .github/workflows/ci.yml + release.yml.

Adversarial pass fixed two hollow-gate findings: the ConsumerProbe stale-same-version-cache
false-pass (now a unique per-run version via an env-var MSBuild property) and the static
closure walk's used-reference blind spot (documented + recursion broadened; the package
probe is authoritative). FrCoverage hardened to ignore commented-out tags.

Build 0-warning under TreatWarningsAsErrors; 338 tests green (was 318).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@moisesja moisesja left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — M8a quality & release gates

Reviewed the full diff. This is genuinely strong infrastructure work: the MetadataLoadContext approach to inspect Credentials.Rdfc's surface without pulling its Newtonsoft transitive is the right call, the ConsumerProbe stale-same-version-cache false-pass is a real and non-trivial finding that was correctly diagnosed and fixed, and the DeferredToLaterPr mechanism is honest rather than silently-green. No correctness blockers; 318→338 with the new tests targeted where they matter. Not approving outright only because of the small items below — none of which need to block merge.

Worth fixing

  • release.yml — quote the version interpolation. Build (L149) and pack (L164) use -p:CredentialsVersion=${{ steps.version.outputs.version }} unquoted. version derives from the pushed tag (${GITHUB_REF_NAME#v}). Tag-push is normally maintainer-only so the risk is low, but a tag name with shell metacharacters/spaces would word-split or inject MSBuild args. Wrap in quotes: -p:CredentialsVersion="${{ steps.version.outputs.version }}".

Worth confirming (likely fine)

  • .snupkg reaches nuget.org. The pack step is labelled .nupkg + .snupkg and the artifact upload globs *.*nupkg, but the push step only globs *.nupkg. This is probably fine because dotnet nuget push auto-pushes an adjacent .snupkg of the same name — but please confirm symbols actually land on nuget.org on the first real release, since the glob doesn't make it explicit.

Minor

  • RoundTripFidelityTests.ProofCount returns 0/1, not a count. It only checks TryGetPropertyValue("proof", …), so a proof that is a 2-element array still returns 1 and the "exactly one proof" assertion passes. Won't catch an accidental multi-proof emission. Tighten to count array elements if that's the intent.
  • LibrarySurface.Configuration (.Parent!.Name) assumes bin/<Config>/net10.0/. The ! suppresses a real NRE that would surface from a static initializer under non-standard output paths (dotnet test --output, publish). Fine in CI; a defensive fallback would save a confusing crash locally.
  • CI job graph. no-newtonsoft and semver are parallel peers with no needs: build-test, and each triggers its own pack/rebuild. Fine for wall-clock, but a single build break shows up as three independent red jobs rather than one root cause. Optional needs: build-test would sharpen the signal.

Happy to push fixes for any of these if you'd like — say the word.


Generated by Claude Code

@moisesja moisesja self-assigned this Jun 24, 2026
… gate CI jobs

Addresses the PR #8 review (all non-blocking):
- release.yml: quote -p:CredentialsVersion="${{...}}" in the build and pack steps so a tag with
  shell metacharacters/spaces cannot word-split or inject MSBuild args; document that the *.nupkg
  push glob auto-publishes the adjacent .snupkg symbols.
- RoundTripFidelityTests.ProofCount: count proof array elements (a 2-proof array now returns 2), so
  the "exactly one proof" assertion catches an accidental multi-proof emission, not just presence.
- LibrarySurface: guard the bin/<Config>/<tfm> assumption (no more `!`-suppressed NRE from a static
  initializer) and try both Debug/Release when locating the Rdfc artifact under a non-standard output.
- ci.yml: no-newtonsoft + semver jobs `needs: build-test`, so a compile break is one root-cause job.

338 tests green; build 0-warning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@moisesja

Copy link
Copy Markdown
Owner Author

Thanks for the review — all five addressed in b94b953 (validated each against the code first; all were accurate, no misreads):

Worth fixing

  • release.yml version quoting-p:CredentialsVersion="${{ steps.version.outputs.version }}" in both the build (L35) and pack steps, so a tag with shell metacharacters/spaces can't word-split or inject MSBuild args.

Worth confirming

  • .snupkg push — confirmed: dotnet nuget push "*.nupkg" auto-publishes the adjacent <id>.<ver>.snupkg to nuget.org's symbol server (no separate push, no double-push risk). Added a comment on the push step documenting this so it's explicit rather than implicit.

Minor

  • ProofCount — now counts proof array elements (a 2-element proof array returns 2), so the "exactly one proof" assertion catches an accidental multi-proof emission, not just presence.
  • LibrarySurface config detection — replaced the .Parent!.Name NRE with a guarded ResolveConfiguration() (defaults sensibly), and the Rdfc artifact lookup now tries both Debug/Release and reports every path it tried on failure — so a non-standard output path yields a clear FileNotFound, not a confusing static-initializer NRE.
  • CI job graphno-newtonsoft and semver now needs: build-test, so a compile break is one root-cause job instead of three red peers.

338 tests green, build 0-warning. Ready for another look.

Captures the workflow correction — opening a PR starts its review cycle; don't stack the
next PR on an unreviewed base. Wait for review/merge before the next unit of work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@moisesja moisesja merged commit 9e041b2 into main Jun 24, 2026
4 checks passed
@moisesja moisesja deleted the feature/m8a-quality-release-gates branch June 24, 2026 03:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant