Skip to content

fix(api-surface): record the shape behind every exported name - #173

Merged
drewstone merged 1 commit into
mainfrom
fix/api-surface-shapes
Aug 21, 2026
Merged

fix(api-surface): record the shape behind every exported name#173
drewstone merged 1 commit into
mainfrom
fix/api-surface-shapes

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Problem

scripts/check-version-bump.mjs, scripts/check-api-surface.mjs and scripts/lib/api-surface.mjs are kept byte-identical in this repository and in agent-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-runtime fixed 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 KnowledgePage or a member joining a published union.

Change

The three files are restored to byte-identical with agent-runtime at its current main, and api-surface.json is 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 contributes package: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 in version-bump-check.test.ts keeps the package names it already had.

Proof

pnpm run build                clean
pnpm run check:api-surface    984 exports across 6 entry points, record current
                              byte-identical after `rm -rf dist && pnpm run build`
pnpm run check:version-bump   consumer surface unchanged at 10.7.0
                              (scripts/ and api-surface.json are outside `files`)
pnpm run lint                 246 files, no fixes
pnpm run typecheck            clean (src + contracts)
pnpm exec vitest run tests/api-surface-record.test.ts     4 passed
pnpm exec vitest run tests/version-bump-check.test.ts    29 passed (was 27)
pnpm test                     671 passed / 128 failed across 22 files
  clean origin/main, same machine: 665 passed / 128 failed across the SAME 22 files.
  Identical failing set; +6 passes are the six tests added here. CI on Linux is the authority.

The three script files hash identically to agent-runtime's:

scripts/check-version-bump.mjs  5a5823807d2d…   identical
scripts/check-api-surface.mjs   c65f59130075…   identical
scripts/lib/api-surface.mjs     6f6667a4fdbc…   identical

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-eval does 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 in version-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 in agent-runtime.

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 tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

@drewstone
drewstone merged commit 1f1fed4 into main Aug 21, 2026
2 checks passed
@drewstone
drewstone deleted the fix/api-surface-shapes branch August 21, 2026 12:13
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.

2 participants