Skip to content

Write the public surface down, so changing it is a deliberate act - #841

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
public-api-guard
Aug 9, 2026
Merged

Write the public surface down, so changing it is a deliberate act#841
Rafael-SOWNet merged 1 commit into
masterfrom
public-api-guard

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Removing a public member breaks every consumer at compile time; adding one is a promise that has to be kept for the rest of the major version. Neither shows up in a test run — the suite only calls the API it knows about, so a member that vanishes takes its own tests with it and the summary line stays green.

RS0016/RS0017 used to cover this. #835 retired the settings — they were dead, and that was right — but the guarantee went with them, and 2.0 is the release where its absence costs the most.

What this adds

PublicApiSurfaceTest measures the surface by reflection and compares it against Sources/Tests/UnitTests/Common/PublicApi.txt2602 members. A difference fails with both lists spelled out, headed by which direction it went.

To accept an intended change:

AM_UPDATE_PUBLIC_API=1 dotnet test Sources/Tests/UnitTests

and commit the file. The diff is then part of review, which is the point — it is not a chore to keep in sync, it is the record of what was promised and when.

Runs in 13 ms.

Choices worth naming

  • Protected members count. A consumer can derive, so they are as much a promise as a public one.
  • One assembly, one framework. The surface genuinely differs per target — the generic-math members are net7.0+ only — so this pins the framework the tests run on and claims nothing about netstandard2.0.
  • By-ref/array/pointer names are normalised. Their Type.FullName embeds the assembly version, so comparing raw would report every such signature as changed on each version bump.

Verified non-vacuous

A guard that cannot fail is worse than none, so I added a public member on purpose:

The public surface no longer matches PublicApi.txt.
ADDED — a promise for the rest of the major version (1):
  AngouriMath.MathS.CanaryDoNotShip(AngouriMath.Entity) : AngouriMath.Entity

What the audit that prompted this found

I diffed 2.0 against the published 1.4.0 package (netstandard2.0, both surfaces dumped by reflection): 2325 → 2579 members, 34 removals.

All 34 are already named in BREAKING-CHANGES.md — no gap. Twelve did not match by name because the document names them collectively, and correctly: "CompilationProtocol's six converter delegates" is exactly six, and the class that owns the other six is listed.

This PR records the surface as it stands; it asserts nothing about whether that surface is right. Two things there are worth a separate decision, not folded in here:

  • 2.0 currently adds 288 public members, of which ~86 are the Core.Transformations layer.
  • New public types in 2.0: the 8 transformation types, 7 nodes (Ceilf, Floorf, Gcdf, Maxf, Minf, Modf, Roundf), MathS.Quantum, and UnrecognizedFunctionParseException.

Tests: 6050 passing, 0 failed, 14 skipped.

Removing a public member breaks every consumer at compile time, and adding one is a
promise that has to be kept for the rest of the major version. Neither shows up in a
test run: the suite only calls the API it knows about, so a member that vanishes
takes its own tests with it and the summary line stays green.

RS0016/RS0017 used to cover this. #835 retired the settings -- they were dead, and
that was right -- but the guarantee went with them, and 2.0 is the release where its
absence costs the most. This restores it without the analyzer package: the surface is
measured by reflection and compared against Common/PublicApi.txt, 2602 members. A
difference fails with both lists spelled out, headed by which direction it went.

To accept an intended change, run once with AM_UPDATE_PUBLIC_API=1 and commit the
result. The diff is then part of review, which is the point -- the file is not a chore
to keep in sync, it is the record of what was promised and when.

Protected members count. A consumer can derive, so they are as much a promise as a
public one.

One assembly, one framework. The surface genuinely differs per target -- the
generic-math members are net7.0 and later only -- so this pins the framework the tests
run on and claims nothing about netstandard2.0.

Checked against a member added on purpose, since a guard that cannot fail is worse
than none:

    ADDED -- a promise for the rest of the major version (1):
      AngouriMath.MathS.CanaryDoNotShip(AngouriMath.Entity) : AngouriMath.Entity

The baseline it records is the surface as it stands, so this commit asserts nothing
about whether that surface is right. What it did establish on the way in: comparing
2.0 against the 1.4.0 package finds 34 removals, and all 34 are already named in
BREAKING-CHANGES.md.

Runs in 13 ms. Tests: 6050 passing, 0 failed, 14 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit 11d8cb6 into master Aug 9, 2026
24 checks passed
Rafael-SOWNet added a commit that referenced this pull request Aug 9, 2026
#841 landed the public-surface baseline after this branch was cut, so the
rename invalidated it. Regenerated with AM_UPDATE_PUBLIC_API=1: 68 lines
change and every one of them is a Latexise entry becoming a Latexize one.

This is the test doing its job -- a rename of 61 overrides is exactly the
kind of change that should not pass silently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rafael-SOWNet added a commit that referenced this pull request Aug 9, 2026
…842)

Stringize and Latexise do the same kind of thing, are declared next to each
other and are overridden per node in the same files -- one American, one
British. Everything else public is -ize: Stringize, Factorize, Serialize,
Deserialize.

Renamed across every surface the name reaches: the method and its 61
overrides, ILatexiseable, the string extension, MathS.Latex's parameter, the
native entry point entity_latexise, the C++ Entity::Latexise, the F#
latexise, and the notebook extension. No forwarding member is left behind.

Sources/Samples/SampleNet5 keeps the old name deliberately: it compiles
against PackageReference AngouriMath 1.3.0, not against this source.

MathS.Quantum.Factorise keeps its spelling; renaming it to Factorize would
give the library two unrelated public Factorize methods. It needs a
distinguishing name instead, which is
#843.

Also corrects a claim in BREAKING-CHANGES.md that nothing parses LaTeX, and
regenerates the PublicApi.txt baseline that
#841 introduced.
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