From a931dc17584f5733b7b790ddc0ff5700cbf9bcfa Mon Sep 17 00:00:00 2001 From: Sawyer Hood Date: Fri, 4 Sep 2026 02:20:41 +0000 Subject: [PATCH] Remove plugin SDK API inventory --- .bb/skills/plugin-guide-maintenance/SKILL.md | 20 +--- AGENTS.md | 2 +- packages/plugin-api-map/package.json | 1 - .../scripts/sdk-api-inventory.d.mts | 9 -- .../scripts/sdk-api-inventory.mjs | 87 -------------- packages/plugin-api-map/sdk-public-api.json | 45 ------- packages/plugin-api-map/test/api-sync.test.ts | 111 ------------------ turbo.json | 7 -- 8 files changed, 6 insertions(+), 276 deletions(-) delete mode 100644 packages/plugin-api-map/scripts/sdk-api-inventory.d.mts delete mode 100644 packages/plugin-api-map/scripts/sdk-api-inventory.mjs delete mode 100644 packages/plugin-api-map/sdk-public-api.json delete mode 100644 packages/plugin-api-map/test/api-sync.test.ts diff --git a/.bb/skills/plugin-guide-maintenance/SKILL.md b/.bb/skills/plugin-guide-maintenance/SKILL.md index b7ace59c8b..dbb9cdf409 100644 --- a/.bb/skills/plugin-guide-maintenance/SKILL.md +++ b/.bb/skills/plugin-guide-maintenance/SKILL.md @@ -1,14 +1,14 @@ --- name: plugin-guide-maintenance -description: Keep the Plugin Guide accurate when a public Plugin SDK change affects its documented contract or an existing Guide annotation changes order, placement, target, or overlay ownership. Use for additions, changes, renames, stabilizations, or removals in @get-bb/plugin-sdk, app.slots.*, or BbPluginApi that affect a Guide card, fixture, symbol list, or SDK inventory, and for annotation-only maintenance. Do not use for internal implementation or API work that leaves the Guide accurate. +description: Keep the Plugin Guide accurate when a public Plugin SDK change affects its documented contract or an existing Guide annotation changes order, placement, target, or overlay ownership. Use for additions, changes, renames, stabilizations, or removals in @get-bb/plugin-sdk, app.slots.*, or BbPluginApi that affect a Guide card, fixture, or symbol list, and for annotation-only maintenance. Do not use for internal implementation or API work that leaves the Guide accurate. --- # Maintain the Plugin Guide The Plugin Guide is bb's public Plugin SDK reference. For a public API change, follow the full workflow. For annotation-only maintenance, start at Maintain -annotation layout, skip the public-API and SDK-inventory sections, and then -follow the annotation-only verification path. +annotation layout, skip the public-API section, and then follow the +annotation-only verification path. ## Confirm a public API change @@ -19,8 +19,8 @@ pnpm exec turbo run build:types --filter=@get-bb/plugin-sdk git diff -- packages/plugin-sdk/package.json packages/plugin-sdk/src ``` -Continue only when the API change affects a Guide card, API symbol list, -fixture, or SDK inventory. If the Guide remains accurate, do not change it. +Continue only when the API change affects a Guide card, API symbol list, or +fixture. If the Guide remains accurate, do not change it. New public members also require: @@ -96,16 +96,6 @@ renumbered, or its target or surrounding layout changes: If responsive layouts cannot share one spatial order, fix the layout or define one stable readable sequence before shipping. -## Refresh the SDK inventory - -Refresh the inventory after the Guide represents a public API change: - -```sh -pnpm exec turbo run update:sdk-inventory --filter=@bb/plugin-api-map -``` - -Review `packages/plugin-api-map/sdk-public-api.json`. Do not edit its hashes. - ## Verify the result For a public API change, run: diff --git a/AGENTS.md b/AGENTS.md index be2777814d..487904c72f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,7 +34,7 @@ ## Plugin API - Any new public plugin API member (a `@get-bb/plugin-sdk/app` export, an `app.slots.*` method, or a `BbPluginApi` property) ships with an `experimental_` name prefix and an entry in [docs/api_to_audit.md](docs/api_to_audit.md) describing what it does and what to audit before stabilizing. Dropping the prefix is the deliberate stabilization step: audit the entry, rename project-wide, and remove it from the doc in the same change. -- The Plugin Guide (the `plugin-api-docs` plugin, rendering `packages/plugin-api-map`) is bb's only plugin API documentation. A new surface needs a card in `packages/plugin-api-map/src/surfaces.ts` naming its SDK symbols in the same change; `packages/plugin-api-map/test/api-sync.test.ts` fails the build when the map and the SDK drift apart. +- The Plugin Guide (the `plugin-api-docs` plugin, rendering `packages/plugin-api-map`) is bb's only plugin API documentation. A new surface needs a card in `packages/plugin-api-map/src/surfaces.ts` naming its SDK symbols in the same change. ## Data Access diff --git a/packages/plugin-api-map/package.json b/packages/plugin-api-map/package.json index e6ff9e9163..8698f75b90 100644 --- a/packages/plugin-api-map/package.json +++ b/packages/plugin-api-map/package.json @@ -20,7 +20,6 @@ "scripts": { "clean": "rimraf tsconfig.tsbuildinfo", "scaffold:surface-entry": "node scripts/scaffold-surface-entry.mjs", - "update:sdk-inventory": "node scripts/sdk-api-inventory.mjs --write", "typecheck": "tsc --noEmit", "test": "vitest run --config vitest.config.ts" }, diff --git a/packages/plugin-api-map/scripts/sdk-api-inventory.d.mts b/packages/plugin-api-map/scripts/sdk-api-inventory.d.mts deleted file mode 100644 index 9fb3a3bf85..0000000000 --- a/packages/plugin-api-map/scripts/sdk-api-inventory.d.mts +++ /dev/null @@ -1,9 +0,0 @@ -export interface SdkPublicApiInventory { - schemaVersion: 1; - entries: Record; -} - -export const INVENTORY_PATH: string; -export function hashDeclarationTokens(source: string): string; -export function createSdkPublicApiInventory(): SdkPublicApiInventory; -export function readSdkPublicApiInventory(): SdkPublicApiInventory; diff --git a/packages/plugin-api-map/scripts/sdk-api-inventory.mjs b/packages/plugin-api-map/scripts/sdk-api-inventory.mjs deleted file mode 100644 index 94d9b6c621..0000000000 --- a/packages/plugin-api-map/scripts/sdk-api-inventory.mjs +++ /dev/null @@ -1,87 +0,0 @@ -import { createHash } from "node:crypto"; -import { existsSync, readFileSync, writeFileSync } from "node:fs"; -import { dirname, join, relative, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -import ts from "typescript"; - -const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const SDK_ROOT = resolve(PACKAGE_ROOT, "../plugin-sdk"); -export const INVENTORY_PATH = join(PACKAGE_ROOT, "sdk-public-api.json"); - -export function hashDeclarationTokens(source) { - // build:types has already parsed and normalized these declarations. Scanning - // their tokens keeps the inventory insensitive to comments and formatting - // without repeating a full TypeScript parse and print for every subpath. - const hash = createHash("sha256"); - const scanner = ts.createScanner( - ts.ScriptTarget.Latest, - true, - ts.LanguageVariant.Standard, - source, - ); - for ( - let token = scanner.scan(); - token !== ts.SyntaxKind.EndOfFileToken; - token = scanner.scan() - ) { - const text = scanner.getTokenText(); - hash.update(`${token}:${Buffer.byteLength(text)}:`); - hash.update(text); - } - return hash.digest("hex"); -} - -function publicTypeEntries() { - const manifest = JSON.parse( - readFileSync(join(SDK_ROOT, "package.json"), "utf8"), - ); - return Object.entries(manifest.exports) - .filter(([subpath]) => !subpath.startsWith("./internal/")) - .map(([subpath, target]) => { - const types = typeof target === "string" ? target : target.types; - if (typeof types !== "string") { - throw new Error(`Public SDK export ${subpath} has no types target`); - } - const path = resolve(SDK_ROOT, types); - if (!existsSync(path)) { - throw new Error( - `Missing built declaration for ${subpath}: ${relative(SDK_ROOT, path)}. Run the @get-bb/plugin-sdk build:types task first.`, - ); - } - return [subpath, { path, types: relative(SDK_ROOT, path) }]; - }) - .sort(([left], [right]) => left.localeCompare(right)); -} - -export function createSdkPublicApiInventory() { - return { - schemaVersion: 1, - entries: Object.fromEntries( - publicTypeEntries().map(([subpath, entry]) => { - return [ - subpath, - { - types: entry.types, - sha256: hashDeclarationTokens(readFileSync(entry.path, "utf8")), - }, - ]; - }), - ), - }; -} - -export function readSdkPublicApiInventory() { - return JSON.parse(readFileSync(INVENTORY_PATH, "utf8")); -} - -if (process.argv[1] === fileURLToPath(import.meta.url)) { - if (!process.argv.includes("--write")) { - throw new Error("Pass --write to update the Plugin Guide SDK inventory"); - } - writeFileSync( - INVENTORY_PATH, - `${JSON.stringify(createSdkPublicApiInventory(), null, 2)}\n`, - ); - console.log(`Updated ${relative(process.cwd(), INVENTORY_PATH)}`); -} diff --git a/packages/plugin-api-map/sdk-public-api.json b/packages/plugin-api-map/sdk-public-api.json deleted file mode 100644 index 1d54009aa0..0000000000 --- a/packages/plugin-api-map/sdk-public-api.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "schemaVersion": 1, - "entries": { - ".": { - "types": "bundled-types/bb-plugin-sdk.d.ts", - "sha256": "f8bc6f12d43847f3bcf9b561f5f4ffeba64afd58bf44cbe929de480c4084f774" - }, - "./ai-services": { - "types": "bundled-types/bb-plugin-sdk-ai-services.d.ts", - "sha256": "da4f251124361cf0d1bf1d21168d81a4f064bbc71aee1e15d89d351e92b34234" - }, - "./app": { - "types": "bundled-types/bb-plugin-sdk-app.d.ts", - "sha256": "181488175ba8447c2aacaadaca0b74d532d02f5b9f4dad16d34e83b9b881013c" - }, - "./host": { - "types": "bundled-types/bb-plugin-sdk-host.d.ts", - "sha256": "355d8480a6d834206efbd02f1e8537d146a4d0dcbe73a9ae99c86c4929d4f7ea" - }, - "./provider-bridge": { - "types": "bundled-types/bb-plugin-sdk-provider-bridge.d.ts", - "sha256": "1f3fd6aa564d84fca2766eb3c98786b7c1838bd5203d526d4902f6c225d80692" - }, - "./provider-bridge/acp": { - "types": "bundled-types/bb-plugin-sdk-provider-bridge-acp.d.ts", - "sha256": "e42e5bcec2fdf41d23ca5799494abc6ac36ebc2699617c5394a7004e27f14135" - }, - "./provider-bridge/testing": { - "types": "bundled-types/bb-plugin-sdk-provider-bridge-testing.d.ts", - "sha256": "5f33951c8498e4c35df82b9dc337d75d9dd4d608f8617b16ea49d67f3eea0f1a" - }, - "./testing": { - "types": "bundled-types/bb-plugin-sdk-testing.d.ts", - "sha256": "2d1cd3fd4a3528a3314c1deca0848071dc160724ad26d77b47bac7ad52f30819" - }, - "./testing/app": { - "types": "bundled-types/bb-plugin-sdk-testing-app.d.ts", - "sha256": "56fac5880ef60e36bab415525a254dffb5b670472c55a5e498aa27bd45414ba6" - }, - "./testing/host": { - "types": "bundled-types/bb-plugin-sdk-testing-host.d.ts", - "sha256": "5c3f3e6971ce1e0bf9e46db7bbada5090e51442e9f2951ecfb84058d3db1b80b" - } - } -} diff --git a/packages/plugin-api-map/test/api-sync.test.ts b/packages/plugin-api-map/test/api-sync.test.ts deleted file mode 100644 index 3a6ef9cb29..0000000000 --- a/packages/plugin-api-map/test/api-sync.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { readFileSync } from "node:fs"; -import { join } from "node:path"; - -import { describe, expect, it } from "vitest"; - -import { SURFACE_GROUPS } from "../src/index"; -import { - createSdkPublicApiInventory, - hashDeclarationTokens, - readSdkPublicApiInventory, -} from "../scripts/sdk-api-inventory.mjs"; - -const SDK_SRC = join(import.meta.dirname, "../../plugin-sdk/src"); - -function read(...parts: string[]): string { - return readFileSync(join(SDK_SRC, ...parts), "utf8"); -} - -const APP_CONTRACT = read("app-contract.ts"); -const CONTRACT_SOURCES = [ - APP_CONTRACT, - read("app.ts"), - read("index.ts"), - read("backend-contract.ts"), - read("testing", "index.ts"), - read("testing", "app.tsx"), - read("testing", "fake-plugin-host.ts"), - read("testing", "fake-sdk.ts"), - read("testing", "host.ts"), -].join("\n"); - -const EXPORTED = new Set( - [ - ...CONTRACT_SOURCES.matchAll( - /^export (?:declare )?(?:abstract )?(?:interface|type|class|function|const|enum) ([A-Za-z_][A-Za-z0-9_]*)/gm, - ), - ].map((match) => match[1]), -); - -const SURFACES = SURFACE_GROUPS.flatMap((group) => group.surfaces); - -describe("public SDK inventory", () => { - it("ignores declaration trivia but preserves every API token", () => { - const compact = - "export interface PluginApi { run(input: string): Promise; }"; - const formatted = ` - /** Public plugin API. */ - export interface PluginApi { - // Run the plugin. - run(input: string): Promise; - } - `; - - expect(hashDeclarationTokens(formatted)).toBe( - hashDeclarationTokens(compact), - ); - expect(hashDeclarationTokens(compact.replace("string", "number"))).not.toBe( - hashDeclarationTokens(compact), - ); - }); - - it("matches every non-internal published declaration subpath", () => { - expect(createSdkPublicApiInventory()).toEqual(readSdkPublicApiInventory()); - }); -}); - -describe("surface-to-SDK links", () => { - it("names only symbols the SDK still exports", () => { - const missing: string[] = []; - for (const surface of SURFACES) { - expect(surface.apiSymbols.length, surface.id).toBeGreaterThan(0); - for (const symbol of surface.apiSymbols) { - if (!EXPORTED.has(symbol)) { - missing.push(`${surface.id}: "${symbol}"`); - } - } - } - expect(missing).toEqual([]); - }); -}); - -function registrationTypes(interfaceName: string): Map { - const body = APP_CONTRACT.match( - new RegExp(`export interface ${interfaceName} \\{([\\s\\S]*?)\\n\\}`), - )?.[1]; - if (!body) throw new Error(`${interfaceName} not found in app-contract.ts`); - const found = new Map(); - for (const match of body.matchAll( - /^ {2}([A-Za-z_][A-Za-z0-9_]*)\(\s*(?:registration:\s*)?([A-Za-z_][A-Za-z0-9_]*)/gm, - )) { - found.set(match[1], match[2]); - } - return found; -} - -describe("registration slot coverage", () => { - it("documents every slot the SDK ships", () => { - const slots = [ - ...registrationTypes("PluginAppSlots"), - ...registrationTypes("PluginAppComposer"), - ...registrationTypes("PluginAppContentScripts"), - ]; - expect(slots.length).toBeGreaterThanOrEqual(15); - - const documented = new Set(SURFACES.flatMap((s) => s.apiSymbols)); - const uncovered = slots - .filter(([, type]) => !documented.has(type)) - .map(([method, type]) => `app.${method}() takes ${type}`); - expect(uncovered).toEqual([]); - }); -}); diff --git a/turbo.json b/turbo.json index 581bb9a2f8..d8c4fec01d 100644 --- a/turbo.json +++ b/turbo.json @@ -35,13 +35,10 @@ "@bb/plugin-api-map#test": { "dependsOn": [ "//#ensure-native-modules", - "@get-bb/plugin-sdk#build:types", "topo" ], "inputs": [ "$TURBO_DEFAULT$", - "$TURBO_ROOT$/packages/plugin-sdk/bundled-types/**", - "$TURBO_ROOT$/packages/plugin-sdk/package.json", // wireframes.test.ts asserts fixture fidelity against the real app // source, and maintenance-skill.test.ts reads the repository skill plus // Plugin Guide source; all are inputs to prevent stale cache hits. @@ -52,10 +49,6 @@ "$TURBO_ROOT$/vitest.shared.ts" ] }, - "@bb/plugin-api-map#update:sdk-inventory": { - "dependsOn": ["@get-bb/plugin-sdk#build:types"], - "cache": false - }, "@bb/plugin-api-map#scaffold:surface-entry": { "cache": false },