chore(api): check in the public API surface so a break is a reviewable diff - #680
Conversation
…e diff ADR 0002 promises source compatibility for Daqifi.Core's public API, but nothing in the build enforced it: removing a public method or adding a member to a public interface produced a green build and a green CI run, and the break surfaced only when a consumer next restored the package. Adds Microsoft.CodeAnalysis.PublicApiAnalyzers to Daqifi.Core with the surface checked in as PublicAPI.Shipped.txt (what v1.7.0 published) and PublicAPI.Unshipped.txt (the 51 members added since). RS0016/RS0017 now fail the build until the files agree with the code, so an API change has to arrive as an explicit, reviewable diff in the same PR. Seeding the two files against v1.7.0 also answered a question nobody could answer before: 51 public members were added since the release and none were removed, so the promise has actually held. No production source changed. RS0041 is switched off for the protoc-generated DaqifiOutMessage.cs only - its 166 oblivious signatures are recorded in the API file with the analyzer's own '~' prefix, and the rule keeps guarding every hand-written public member. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
PR Summary by QodoTrack and enforce the Daqifi.Core public API surface
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1.
|
The comments on EveryPublicTypeInTheAssembly_IsDeclaredInAnApiFile claimed it catches stale API files whenever the analyzer is suppressed. It compares public type names in one direction only, so a changed signature, a changed nullability annotation or a removed member all leave it green. Narrowed the claim to what the test does rather than widening the test: a member-level comparison here would be a second implementation of the analyzer's entry format that could disagree with the first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 9f0a5d4 |
|
Qodo-clean, CI green — ready for review. Head |
What was wrong
ADR 0002 promises that
Daqifi.Corekeeps source compatibility for its public API — 231 public types, ~2,700 members. Nothing in the build checked that. Delete a public method, narrow a parameter type, add a member to a public interface, and you get a green build and a green CI run. The break shows up later, whendaqifi-desktop,Daqifi.Mcpor an outside integrator next restores the package. That is how #557 was found — by an adversarial audit on a PR, not by tooling — and it is the reason #612 and #616 are parked: everyone agrees their fix "breaks the API", but there was no artifact saying what the API even is.How it was fixed
The public surface is now checked in, as
PublicAPI.Shipped.txt(what v1.7.0 published) andPublicAPI.Unshipped.txt(what has been added since), andMicrosoft.CodeAnalysis.PublicApiAnalyzersfails the build whenever the code and those files disagree. Changing the API is still allowed — it just has to arrive as an explicit diff to the API file in the same PR, where a reviewer can see it.Seeding the files against the v1.7.0 tag rather than against
mainalso answered a question nobody could answer before: 51 public members have been added since the release and none removed. The promise has held in practice.Two things worth pushing back on, so they are not buried:
Daqifi.Core.csprojis +9 lines;PublicAPI.Shipped.txtis 2,669. That file is a generated baseline and is meant to be skimmed, not read. The reviewable part is the csproj, the.editorconfigstanza,CONTRIBUTING.md, andPublicAPI.Unshipped.txt— 51 lines that should look exactly like the public API added since v1.7.0.EnablePackageValidation(the second half of chore(api): nothing tracks the public API surface, but ADR 0002 makes source compatibility a promise #636) is deliberately not here, so this does not close the issue. It runs atdotnet packtime, and CI never packs — only the release workflow does — so it would catch breaks at release rather than in the PR. It also tripsCP0003immediately, because the repo has no<Version>and so packs locally as1.0.0, which is "lower than" the 1.7.0 baseline. That wants its own decision about repo versioning, not a suppression tacked onto this PR.Detail
DaqifiOutMessage.csis skipped by the analyzer's own code fix, so its 191 entries were added by hand from the RS0016 diagnostics. Noted inCONTRIBUTING.mdfor whoever regenerates the protos next. (Its types sit in the global namespace, which is its own small surprise.)RS0041(public members should not use nullable-oblivious types) is switched off for that one generated file. It fires 166 times there and the fix — annotating protoc's output — is not ours to make. The obliviousness is not lost: those entries carry the analyzer's~prefix, so a change in their nullability still shows as an API diff. The rule stays active for every hand-written public member.RS0026/RS0027(overloads with optional parameters) do not fire on this baseline, but they will on a future addition that follows the repo's existing optional-parameter convention. Left at default rather than pre-suppressed; that is a decision for the PR that first hits it.Verified
dotnet test Daqifi.Core.sln -warnaserrorgreen on both frameworks: net9.0 4022 passed / 2 skipped, net10.0 4022 passed / 2 skipped (+7 new cases).PublicApiTrackingTestsguard the analyzer's wiring — delete the twoAdditionalFileslines and the analyzer silently finds nothing to compare against and reports nothing at all. Each was checked to fail on a green build; two candidate tests were dropped for failing that check (duplicates are already RS0025, and droppingPrivateAssetsalready breaks the build).mainand from this branch in the same directory: identical package contents, byte-identical XML docs, identical assembly sizes, and no new package dependency./dev/cu.usbmodem1101: connect → status → AI0/AI1 → 100 Hz → 3 s CSV capture → clean disconnect, exit 0. Non-destructive.Addresses #636 (leaves the
EnablePackageValidationhalf open, see above).Not merging — for review.