fix(api-surface): record the shape behind every exported name - #173
Merged
Conversation
The three surface-gate files are kept byte-identical here and in agent-runtime, because they read everything repo-specific out of the manifests they inspect. agent-runtime#953 fixed a blind spot in them: the record compared export names only, so it answered "consumer surface unchanged" for a change that removed a public field from an exported interface and for one that added a member to an exported union. This repository carried the unfixed copy and the same gap. The three files are restored to byte-identical, and api-surface.json is regenerated from this package's own build. Each name now records its kind and a shape digest taken over the built declaration, with comments removed, whitespace collapsed, the declaration's own local name blanked, and every type reference rewritten to a stable token. A shape move is classified breaking. The two test files move with them unchanged.
tangletools
approved these changes
Aug 21, 2026
tangletools
left a comment
Contributor
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — bb9fd9a7
This PR was opened by the trusted drewstone account.
This approval is provisional and was applied by the local stand-in because the pr-reviewer webhook host is unreachable (2026-08-21). CI on this head is fully green. The full PR reviewer audit re-runs via the resweep when the service returns and will publish findings if it detects issues.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
scripts/check-version-bump.mjs,scripts/check-api-surface.mjsandscripts/lib/api-surface.mjsare kept byte-identical in this repository and inagent-runtime— the header of the first one says so, and says why: they read everything repo-specific out of the manifests they inspect, so a bug fixed in one copy is a bug still live in the other two.agent-runtimefixed one yesterday (tangle-network/agent-runtime#953). The record compared export names, never the shape behind a name, so it answered "consumer surface unchanged" for a pull request that removed a public field from an exported interface, and for one that added a member to an exported union. Measured across the eleven pull requests merged there that day, the fixed comparison reported exactly two changes — both real, zero false positives.This repository carries the unfixed copy and the same blind spot: nothing here would notice a field leaving
KnowledgePageor a member joining a published union.Change
The three files are restored to byte-identical with
agent-runtimeat its currentmain, andapi-surface.jsonis regenerated from this package's own build.Each name now records its kind and a shape digest —
"AdaptiveDecision": "value b5e46c8679f8"— taken over the built declaration with comments removed, whitespace collapsed, the declaration's own local name blanked, and every type reference rewritten to a stable token: a symbol this package exports contributes its public name, one re-exported from a dependency contributespackage:name, one declared but not exported contributes its own digest. A shape move is classified breaking, because telling an added optional field from a removed required one is a subtyping question the record does not answer.The two test files move with them, unchanged from
agent-runtime, and the fixture inversion-bump-check.test.tskeeps the package names it already had.Proof
The three script files hash identically to
agent-runtime's:No version bump: nothing a consumer installs changed.
Simplification
Simplification: the three shared files return to one copy in two repositories, so the next fix to either lands in both; the shape half of the record replaces the reviewer judgement that a name-only record left as the only defence.
Net: +498 / -66 lines of script and test across 5 files, plus the regenerated record; 0 paths removed, 1 blind spot closed, 3 files back to one copy.
Not done here:
agent-evaldoes not carry these scripts at all, so the header's claim that they are identical in three repositories is aspirational. Bringing them there is a separate decision — that repository has its own export-census work in flight.Tests: +4 in a new
api-surface-record.test.ts(a removed field moves its type's digest and nothing else's, a union member added moves it, a doc comment or reformat does not, a bundler rename does not) and +2 inversion-bump-check.test.ts(a shape move with no bump is refused and names the symbol; a base record that states no shape is not compared), -0 deleted. All six are the ports of the tests that closed the defect inagent-runtime.