Skip to content

build: add a public-API baseline and package validation#280

Merged
Reefact merged 2 commits into
mainfrom
claude/issue-221-discussion-oegtpv
Jul 21, 2026
Merged

build: add a public-API baseline and package validation#280
Reefact merged 2 commits into
mainfrom
claude/issue-221-discussion-oegtpv

Conversation

@Reefact

@Reefact Reefact commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

Install a committed public-API baseline (Microsoft.CodeAnalysis.PublicApiAnalyzers) and package validation (EnablePackageValidation) on the four shipping libraries, so an accidental breaking change to the public surface — a removed overload, a narrowed return type, a renamed member — fails the build until the same change records it. Pre-release is the cheapest moment to install this: the first published surface becomes the baseline.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Analyzer / diagnostic change
  • Tests
  • Documentation
  • Build / CI / tooling

No product code changed.

Changes

  • Directory.Packages.props: pin Microsoft.CodeAnalysis.PublicApiAnalyzers 5.6.0 (CPM, alongside the other Microsoft.CodeAnalysis.* 5.6.0 packages).
  • build/PublicApiBaseline.props (new, mirrors build/Net472TestFloor.props): references the analyzer (PrivateAssets=all), wires per-target-framework PublicAPI.Shipped.txt / PublicAPI.Unshipped.txt, enables EnablePackageValidation, and disables RS0026/RS0027 (see note).
  • Imported it in the four packable libraries: FirstClassErrors, FirstClassErrors.Testing, FirstClassErrors.RequestBinder (the lib train) and Dummies (the dum train).
  • Committed baselines per project × target framework. Dummies tracks its two divergent surfaces separately (341 entries on netstandard2.0, 411 on net8.0 — the net8-only generators AnyDateOnly / AnyTimeOnly / AnyInt128 / AnyUInt128 / AnyHalf).
  • CONTRIBUTING.md (+ French translation): a "Public API baseline" section on how to accept an intended surface change.

Scope. #221 scoped this to Dummies; it is extended to the lib train because those packages carry the same SemVer promise and the mechanism is identical (no separate lib-train issue existed). Unlike Dummies, the lib train already has a published baseline (lib-v0.1.0-preview.1) to gate against later.

RS0026/RS0027 are optional-parameter overload design rules — distinct from the baseline-drift rules this PR is about. They fire on the deliberate central fluent APIs (Outcome.Then/Recover/Finally/Try/Create/Error, Any.Reproducibly), where enforcing them would demand a breaking overload redesign (overlapping the pre-v1 Outcome API-symmetry review, #242). Disabled deliberately (scoped, with a rationale comment) and surfaced for @Reefact rather than acted on silently.

Testing

  • dotnet build FirstClassErrors.sln — green, including under the CI warnings-as-errors ratchet (GITHUB_ACTIONS=true).
  • dotnet test FirstClassErrors.sln1434 passed, 0 failed, 0 skipped (10 test projects).
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests) — 132 passed.

Additional checks:

  • Gate proven — an undeclared public symbol fails the build with RS0016 on both target frameworks; removing it goes green again.
  • Cross-TFM package validation passes on dotnet pack (net8.0 is a clean superset of netstandard2.0, no suppressions needed).
  • Baseline path exercised — packing FirstClassErrors against 0.1.0-preview.1 restores the baseline package and ApiCompat flags the pre-1.0 drift. Left baseline-less by default (PackageValidationBaselineVersion unset) so intended pre-1.0 breaks don't block CI.

Documentation

  • Public API / error documentation updated
  • README / doc/ updated
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

CONTRIBUTING.md and its French translation doc/handwritten/for-users/CONTRIBUTING.fr.md both gained the "Public API baseline" section (bilingual parity kept). The committed PublicAPI.*.txt files are themselves the API record.

Architecture decisions

  • No architectural decision in this pull request
  • New decision recorded — ADR drafted as Proposed: ADR-____
  • Supersedes an existing ADR — successor proposed, status not flipped: ADR-____
  • ⚠️ Conflicts with an existing ADR — flagged for the maintainer: ADR-____

Checked against the ADR base (ADR-0004). ADR-0011 (Dummies as a standalone, zero-dependency package) verified compliant: the analyzer is PrivateAssets=all, and the packed Dummies nuspec has empty dependency groups and ships only Dummies.dll. The baseline mechanism is release-engineering hygiene — the Dummies architecture audit (§4.7 / §11 item 7) classified it as a "guard", not an ADR-worthy decision — so no ADR is drafted. The one embedded policy choice (disabling RS0026/RS0027) is documented in-code and left to @Reefact; judged below the ADR threshold but flagged here for a deliberate call.

Related issues

Closes #221


Generated by Claude Code

claude and others added 2 commits July 21, 2026 21:10
The shipping libraries commit to SemVer but had no automated breaking-change
gate: a repo-wide search found no PublicApiAnalyzers, no package validation,
no ApiCompat baseline. A removed overload, a narrowed return type, or a
renamed member could ship silently under a version that promises
compatibility. Pre-release is the cheapest moment to install this, since the
first published surface becomes the baseline.

Wire both guards once in build/PublicApiBaseline.props, imported by the four
packable libraries -- the lib train (FirstClassErrors, FirstClassErrors.Testing,
FirstClassErrors.RequestBinder) and Dummies:

* PublicApiAnalyzers tracks each project's surface in committed
  PublicAPI.Shipped.txt / PublicAPI.Unshipped.txt files, one pair per target
  framework (Dummies' net8.0 leg carries the modern generators netstandard2.0
  lacks). RS0016/RS0017 stay warnings locally and become errors under the CI
  warnings-as-errors ratchet, so a surface change fails the build until the
  same change records it.
* EnablePackageValidation runs ApiCompat on pack -- baseline-less for now (the
  same-package cross-TFM check), ready to gate against a published version via
  PackageValidationBaselineVersion once the surface stabilises.

The issue scoped this to Dummies; it is extended to the lib train because those
packages carry the same SemVer promise and the mechanism is identical.

RS0026/RS0027 (the optional-parameter overload design rules) are disabled
deliberately: they fire on the deliberate central fluent APIs
(Outcome.Then/Recover/Finally/Try, Any.Reproducibly), where enforcing them
would demand a breaking redesign -- a separate call for the maintainer, not a
baseline chore.

Refs: #221

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LuKEgwmiNthNBXEXidrbhV
@Reefact
Reefact merged commit 0cd90e0 into main Jul 21, 2026
15 checks passed
@Reefact
Reefact deleted the claude/issue-221-discussion-oegtpv branch July 21, 2026 21:48
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.

Dummies: add a public-API baseline and package validation before the first release

2 participants