API Contract Governance: Diffing the Public Surface In short: Renaming an input is invisible to unit tests, visual tests, and accessibility tests — and it breaks every consumer's build. Snapshot the public surface, diff it on every pull request, and make the gate read signal inputs rather than decorators. We have gates for logic, pixels, and accessibility. There is one category none of them touch. A developer tidying up the shared library renames to . The library compiles. Every story still renders identically, so visual tests pass. Accessibility is unchanged. The pull request is green in every way we have measured. The next morning, the dispatch dashboard and the farmer portal both fail to build, because their templates still bind . Snapshot the surface Chapter 4 established that is a promise. This gate holds you to it. The critical detail — and the thing most published examples get wrong — is that Angular 22 components declare inputs as initialisers, not decorators : There is no here. A script that filters on finds nothing on a modern component and reports an empty API — which means it passes every diff and protects nothing. Silently. That is the worst possible failure for a guard. Here is a version that reads what is actually there: Three decisions worth copying: Walk , not every file. The surface is what is exported. Chapter 4's whole argument, expressed as a script. Sort the members. Otherwise reordering a class produces a spurious diff. Capture the selector. Renaming to breaks every template and is not otherwise visible. Decide what is breaking Note "became required" is treated as breaking. It is easy to miss: → adds no name but breaks every consumer who relied on the default. Wire it up A is cheaper and less error-prone than checking the same repository out twice into nested paths. Verify the gate itself A guard that silently reports nothing is worse than no guard, because it produces confidence. Test the snapshotter: If this test ever returns an empty array, the gate has stopped working. That is the failure mode to guard against. What this does not catch Being explicit, so nobody over-trusts it: Change Caught? --- --- Input removed, renamed, or retyped Yes Optional input becomes required Yes Component selector renamed Yes Export removed Yes A CSS custom property renamed No — see below Projection slot selector changed No An output's timing or frequency changed No Rendered DOM restructured No — but visual tests catch it The CSS gap is real, because Chapter 6 made tokens part of the API. A simple grep-based snapshot closes it: Diff that file the same way. A removed line is a breaking change. "But I thought TypeScript would catch a breaking change" Within one repository it does, completely — rename an input and every template binding fails to compile. That experience is what makes this belief so reasonable. The gap is that your consumers are not in your compilation. When the library team builds , the compiler checks the library. It does not check the dispatch dashboard, and it certainly does not check twenty repositories that install a published package. From the library's point of view, renaming an input is a purely internal change — nothing in its own source refers to the old name any more, so everything type-checks. The failure surfaces at the wrong time, in the wrong place, for the wrong people: --- --- --- Where the mistake is made The library PR Preventable here Where the compiler is happy The library PR No signal Where it breaks 20 consumer repositories Not your build When Whenever each team next upgrades Days or weeks later Who debugs it Twenty teams, separately Each one from scratch In a monorepo you get partial protection — will build the consumers in the same workspace. That is genuinely useful, and it is why the consumers in this course would catch it. But it does not help with a published package, it does not help teams who have pinned an older version, and it tells you that a build broke rather than which promise you broke. The API diff is different in kind. It reports the change in the vocabulary of the contract — — before the code merges, in the repository where the decision is being made, to the person making it. That framing is what makes Chapter 9's deprecation workflow possible: you cannot deprecate gracefully if you find out you broke something after you shipped it. Recap Angular 22 inputs are initialisers, not decorators. A script filtering on finds nothing and passes silently. Walk — the exported surface is the promise. Sort members and capture the selector , so the diff is stable and complete. "Became required" is breaking , and it adds no new name to spot. Test the snapshotter. A guard returning an empty API is worse than no guard. Snapshot CSS custom properties too — Chapter 6 made them API. TypeScript checks your repository, not your consumers'. That is the whole gap. Next: turning all this evidence into one decision.