From 5c8273a7d67f28c20c7f7b2df2266f5b9a6ca66a Mon Sep 17 00:00:00 2001 From: Teal Larson Date: Mon, 3 Aug 2026 12:03:41 -0400 Subject: [PATCH 1/3] docs(generator): document data provenance and source-of-truth rules ARCHITECTURE.md described the pipeline but not where any individual field comes from, so tracing a wrong category or a missing callout back to its origin meant reading the generator end to end. Adds a pipeline diagram, a per-field provenance table, and the constraints that are not visible from any single file: - The output directory is also an input. Hand-authored documentationChunks, customImports, and subPages have no upstream source and survive only through previous-output carry-forward, so --force-regenerate and --overwrite-output discard them as a side effect. - src/shared/toolkit-schemas.ts is the one contract, validated on write by the generator and on read by the app. - metadata.category and docsLink come from a pinned design-system dependency and determine the page URL, making a version bump a routing change. An unrecognized category throws rather than falling back. - Toolkits are derived by grouping tools, not fetched as a list. - Absence and corruption are different failures, enforced on both halves. Also records the skip-toolkits/remove-toolkits distinction, the cached loader, and the shared modules under src/shared/. Co-Authored-By: Claude Opus 5 (1M context) --- toolkit-docs-generator/ARCHITECTURE.md | 230 ++++++++++++++++++------- 1 file changed, 169 insertions(+), 61 deletions(-) diff --git a/toolkit-docs-generator/ARCHITECTURE.md b/toolkit-docs-generator/ARCHITECTURE.md index 86654dd08..afece904b 100644 --- a/toolkit-docs-generator/ARCHITECTURE.md +++ b/toolkit-docs-generator/ARCHITECTURE.md @@ -1,87 +1,189 @@ # Toolkit docs generator architecture -This document explains how the toolkit docs generator assembles JSON output and how that output is rendered in the docs site. +This document explains where toolkit documentation data comes from, how the generator +assembles it, and how the docs site renders it. ## Overview -The generator builds toolkit JSON files from multiple sources and writes them to an output directory. The docs site reads these JSON files and renders them into pages at build time. - -The generator does **not** render HTML. It produces structured JSON and optional markdown snippets that the app renders later. - -## Data flow - -1. Fetch tool definitions from the Engine API or Arcade API. -2. Load toolkit metadata from the design system or mock metadata. -3. Load custom sections from JSON files (optional). -4. Merge all data into `MergedToolkit` objects. -5. Write a JSON file per toolkit and an `index.json` file. -6. Optionally verify output and compute diffs. +The generator builds one JSON file per toolkit from several sources and commits them to +`data/toolkits/`. The docs site reads those committed files and renders them into pages at +build time. + +The generator does **not** render HTML, and it does **not** run during a Vercel build. The +nightly workflow commits JSON and navigation changes through a pull request; Vercel then +runs the root `pnpm build` and compiles the committed files with Next.js. That separation is +deliberate — a docs deploy never depends on the Engine being reachable. + +## Pipeline + +```mermaid +graph TD + Engine[Arcade Engine v1 tool_metadata] --> Merger[DataMerger] + DesignSystem[Design system package] --> Merger + LLM[Anthropic summaries and examples] --> Merger + Merger --> Artifact[Committed toolkit JSON and index] + Artifact -.->|previous output| Merger + Artifact --> Sidebar[Generated navigation meta files] + Artifact --> Build[next build] + Sidebar --> Build + Build --> Pages[Toolkit pages and API route] + Build --> Exports[Markdown export, llms.txt, sitemap] +``` + +The dotted edge is the most important thing on this diagram: **the output directory is also +an input.** See [Source-of-truth rules](#source-of-truth-rules). + +## Where each field comes from + +Roughly 117 toolkits and 8,000 tools, refreshed nightly at 11:00 UTC and on +`porter_deploy_succeeded`. + +| Field | Source | Changes when | +|---|---|---| +| `tools[].name`, `description`, `parameters`, `output`, `secrets` | Arcade Engine | Engine deploys a toolkit | +| `tools[].auth.providerId`, `providerType`, `scopes` | Arcade Engine | same | +| `tools[].metadata.behavior` (`readOnly`, `destructive`, `idempotent`, `openWorld`) | Arcade Engine | same | +| `version` | Arcade Engine — parsed from `fullyQualifiedName` after the `@` | same | +| `description` (toolkit) | Arcade Engine — `tools[0].toolkitDescription ?? tools[0].description` | same | +| `label` | design system, falling back to the toolkit description, then a humanized id | dependency bump | +| `metadata.category`, `iconUrl`, `type`, `isPro`, `isBYOC`, `isComingSoon`, `isHidden`, `docsLink` | design system | dependency bump | +| **the page URL** | derived from `metadata.category` + the last segment of `docsLink` | dependency bump | +| `summary` | Anthropic, keyed on `buildToolkitSummarySignature` | toolkit signature changes | +| `tools[].codeExample`, `secretsInfo` | Anthropic, keyed on `buildComparableToolSignature` | tool signature changes | +| `documentationChunks`, `customImports`, `subPages` | **hand-authored, stored only in the committed JSON** | a human edits the file | + +`buildComparableToolSignature` deliberately ignores descriptions and normalizes enum and +output representations, so cosmetic upstream churn does not trigger LLM regeneration. + +When the design system has no entry for a toolkit, `getDefaultMetadata` supplies a +placeholder. Every field in it is a guess rather than a fact, so it forces `isHidden: true` +and nothing routes to it, and `--require-complete` — which the nightly workflow passes — +turns the omission into a failure that names the toolkit. + +## Source-of-truth rules + +These are the constraints that are not obvious from reading any single file. + +### The committed JSON is the system of record for hand-authored prose + +`documentationChunks`, `customImports`, and `subPages` have no upstream source. The +generator supports loading them from a file (`--custom-sections`), but the nightly workflow +does not pass that flag, so on every run the custom-sections source is empty and these +fields survive only because the merger carries the previous value forward when the incoming +value is empty (`mergeCustomSectionsArrays` in `src/merger/data-merger.ts`). + +Consequences to respect when changing the generator: + +- **Anything that disables previous-output loading discards prose.** `--force-regenerate` + and `--overwrite-output` both set the previous-output directory to `undefined`, which + removes the only copy of this content from the run. `--force-regenerate` is documented as + refreshing examples and summaries; dropping prose is a side effect, not an intent. +- `src/diff/previous-output.ts` carries these three fields forward **even when the previous + file fails schema validation**, for the same reason. That leniency is what keeps a schema + mismatch from wiping prose, and should not be "cleaned up" without first giving prose + another home. + +### One schema, imported by both halves + +`src/shared/toolkit-schemas.ts` holds the Zod definitions for the output format. The +generator validates against them on write and the docs site validates against them on read, +so there is no second hand-written description of the same data to drift against. The app's +types are `z.infer` of those schemas, and `src/shared/toolkit-primitives.ts` holds the id, +slug, and category helpers both halves share. + +### A design-system bump can move page URLs + +`metadata.category` and `metadata.docsLink` come from `@arcadeai/design-system`, which is a +pinned dependency. A version bump is therefore a routing change: recategorizing a toolkit +changes its canonical URL and needs a redirect. Review metadata diffs before bumping it. + +A category the site does not recognize is a build failure, not a fallback. There is no +`others` catch-all — `normalizeCategory` throws, because every category needs a matching +route directory, and silently absorbing an unknown one produced clickable catalog cards +pointing at pages that could not exist. + +### Toolkits are derived from tools, not listed + +There is no "list toolkits" call. `fetchAllToolkitsData` fetches every tool and groups by +the first segment of `qualifiedName`, so a toolkit exists only if it has at least one tool. +`/v1/tool_metadata_summary` does return an authoritative toolkit list with per-toolkit tool +counts; `fetchToolkitsSummary` implements it but nothing calls it today. + +### Absence and corruption are different failures + +A missing file is normal — an optional toolkit, and the reader returns `null`. A file that +is present but unparseable or schema-invalid is a defect that fails the build, because this +data arrives through an automated pull request where a silent page deletion would ship. +Both halves enforce this: `src/generator/output-verifier.ts` reports read, syntax, and +schema errors distinctly on write, and `app/_lib/toolkit-data.ts` throws with the file path +and the underlying issue on read. ## Core components ### Data sources -- `EngineApiSource` fetches tool metadata from the Engine API. -- `ArcadeApiSource` fetches tool metadata from the Arcade API. -- `DesignSystemMetadataSource` loads toolkit metadata from `@arcadeai/design-system`. -- `CustomSectionsFileSource` loads custom documentation chunks from a JSON file. -- `CombinedToolkitDataSource` merges tools and metadata into one interface. +- `EngineApiSource` — tool definitions from `GET /v1/tool_metadata`, paginated at 1000 per + page, Bearer auth. Selected by `--api-source tool-metadata`; this is what CI uses. +- `ArcadeApiSource` — tool definitions from `GET /v1/tools`. Different response schema, no + server-side toolkit filtering. Selected by `--api-source list-tools`. +- `DesignSystemMetadataSource` — toolkit metadata from `@arcadeai/design-system`. Matching is + by normalized id with a `*Api` → provider-id fallback. +- `CustomSectionsFileSource` — hand-authored chunks from a JSON file, when `--custom-sections` + is supplied. +- `CombinedToolkitDataSource` — joins tools and metadata behind one interface, so the rest of + the pipeline does not know they are separate sources. +- Mock equivalents (`src/sources/mock-*.ts`) back `--api-source mock`, which needs no Engine + or Anthropic credentials and is the right way to exercise the pipeline locally. + +Two toolkit lists control what gets processed, and they are not synonyms: +`skip-toolkits.txt` (`--ignore-file`) skips a toolkit and leaves any previously generated +output in place; `remove-toolkits.txt` (`--exclude-file`) skips it **and** deletes its +output file. ### Merger -`DataMerger` creates `MergedToolkit` objects by combining tools, metadata, and custom sections. - -It also: -- Computes tool signatures for change detection. -- Generates optional tool examples and summaries with LLMs. -- Tracks warnings and failed tools. +`DataMerger` builds `MergedToolkit` objects from tools, metadata, and custom sections. It +also computes tool signatures for change detection, generates examples and summaries through +the LLM, runs the secret-coherence scan, and collects warnings, failed tools, and which +toolkits fell back to placeholder metadata. ### Generator -`JsonGenerator` writes the final output: - -- `.json` for each toolkit -- `index.json` for lookup and metadata - -It can also verify output consistency with `OutputVerifier`. +`JsonGenerator` writes `.json` per toolkit plus `index.json`, using atomic +writes and rejecting unsafe or colliding filenames. `OutputVerifier` re-reads the output +directory and validates it. ### Diffing -`ToolkitDiff` compares new output to previous output. It reports: -- new toolkits -- removed toolkits -- modified toolkits -- version-only changes +`ToolkitDiff` compares new output against previous output and reports new, removed, +modified, and version-only changes. `--skip-unchanged` uses it to regenerate only what +changed, which is what keeps the nightly pull request reviewable. ## Rendering in the docs site -The generator output is consumed by the Next.js app: - -- The app loads JSON from `toolkit-docs-generator/data/toolkits/`. -- `generateStaticParams` enumerates the toolkit routes and disables unknown dynamic parameters. -- Custom documentation chunks are rendered as MDX in the UI. - -If you need HTML output, add a separate build step in the app. The generator intentionally avoids HTML to keep the pipeline deterministic. - -## Vercel build - -The generator does not run during a Vercel build. The generation workflow commits -the JSON files and navigation changes through a pull request. Vercel then runs the -root `pnpm build` command and compiles the committed files with Next.js. - -`app/_lib/toolkit-static-params.ts` enumerates routes from `index.json` and the -per-toolkit files. `app/_lib/toolkit-data.ts` reads the same files for page -rendering and the `/api/toolkit-data/[toolkitId]` route. The root layout no -longer reads request headers — the locale it needs is a hardcoded constant, -since `proxy.ts` redirects every request to an `/en` path — so Vercel can -statically render the toolkit routes at build time from the committed JSON. +- `app/_lib/toolkit-data.ts` reads the committed JSON for page rendering and for the + `/api/toolkit-data/[toolkitId]` route. `loadAllToolkitData` is wrapped in React's `cache()` + so a build reads the directory once and every caller shares one map. +- `app/_lib/toolkit-static-params.ts` enumerates routes from `index.json` and the per-toolkit + files, and disables unknown dynamic parameters. +- Pages live at `/en/resources/integrations//` behind a `[toolkitId]` dynamic + route per category, and prerender to static HTML at build time. +- Initial HTML carries a stripped summary; per-tool detail is fetched from + `/api/toolkit-data/[toolkitId]` on expand, which keeps the largest reference pages under + Googlebot's 2 MB crawl limit. +- `documentationChunks` are rendered as MDX. Two consumers implement the + location/position injection model: `documentation-chunk-renderer.tsx` for the page and + `app/_lib/toolkit-markdown.ts` for the markdown export. Both must stay in agreement. + +If you need HTML output from the generator, add a separate build step in the app. The +generator intentionally avoids HTML to keep the pipeline deterministic. ## Search indexing -Search uses an external Algolia crawler. There is no Pagefind or local search -index build step in this repository. After deployment, the crawler indexes the -rendered site. `app/_components/algolia-search.tsx` queries that index with the -public, read-only values configured through these Vercel environment variables: +Search uses an external Algolia crawler. There is no Pagefind or local search index build +step in this repository. After deployment, the crawler indexes the rendered site. +`app/_components/algolia-search.tsx` queries that index with the public, read-only values +configured through these Vercel environment variables: - `NEXT_PUBLIC_ALGOLIA_APP_ID` - `NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY` @@ -89,10 +191,16 @@ public, read-only values configured through these Vercel environment variables: ## Key files -- `src/sources/engine-api.ts` — tool metadata from Engine API +- `src/shared/toolkit-schemas.ts` — the output contract, imported by generator and app +- `src/shared/toolkit-primitives.ts` — shared id, slug, and category helpers +- `src/shared/toolkit-data-dir.ts` — data directory resolution (Node-only, kept separate so + it never enters a client component's import graph) +- `src/sources/engine-api.ts` — tool metadata from the Engine - `src/sources/toolkit-data-source.ts` — unified data source -- `src/merger/data-merger.ts` — merge pipeline +- `src/merger/data-merger.ts` — merge pipeline, signatures, carry-forward +- `src/diff/previous-output.ts` — lenient re-parse of committed output - `src/generator/json-generator.ts` — output writer - `src/generator/output-verifier.ts` — output validation - `src/diff/toolkit-diff.ts` — change detection - `src/cli/index.ts` — CLI entry point +- `scripts/sync-toolkit-sidebar.ts` — writes the integrations `_meta.tsx` navigation From c2645b4c6575c63724fcafad3998081bf51233f0 Mon Sep 17 00:00:00 2001 From: Teal Larson Date: Mon, 3 Aug 2026 13:52:52 -0400 Subject: [PATCH 2/3] docs: add rendered toolkit pipeline diagram --- toolkit-docs-generator/ARCHITECTURE.md | 2 ++ .../assets/toolkit-docs-pipeline.png | Bin 0 -> 93856 bytes 2 files changed, 2 insertions(+) create mode 100644 toolkit-docs-generator/assets/toolkit-docs-pipeline.png diff --git a/toolkit-docs-generator/ARCHITECTURE.md b/toolkit-docs-generator/ARCHITECTURE.md index afece904b..18a760238 100644 --- a/toolkit-docs-generator/ARCHITECTURE.md +++ b/toolkit-docs-generator/ARCHITECTURE.md @@ -30,6 +30,8 @@ graph TD Build --> Exports[Markdown export, llms.txt, sitemap] ``` +![Rendered toolkit documentation pipeline showing the data sources, merge step, committed artifacts, and build outputs](./assets/toolkit-docs-pipeline.png) + The dotted edge is the most important thing on this diagram: **the output directory is also an input.** See [Source-of-truth rules](#source-of-truth-rules). diff --git a/toolkit-docs-generator/assets/toolkit-docs-pipeline.png b/toolkit-docs-generator/assets/toolkit-docs-pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..7411f4af59803f34b3f5f89ea27a14cb0410cf76 GIT binary patch literal 93856 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

jvVrgZ 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

XSg8UIXO^ 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 Date: Mon, 3 Aug 2026 14:43:45 -0400 Subject: [PATCH 3/3] fix: harden docs data pipeline review findings --- app/_lib/toolkit-data.ts | 16 +++- tests/toolkit-data-cache.test.ts | 30 ++++++- tests/toolkit-generation-site.test.ts | 81 +++++++++++++++++++ .../scripts/check-stale-summaries.ts | 52 ++++++++---- .../src/cli/exclusion-cleanup.ts | 54 ++++++++----- .../src/diff/previous-output.ts | 52 ++++++------ 6 files changed, 219 insertions(+), 66 deletions(-) create mode 100644 tests/toolkit-generation-site.test.ts diff --git a/app/_lib/toolkit-data.ts b/app/_lib/toolkit-data.ts index f724b03b2..653acf44e 100644 --- a/app/_lib/toolkit-data.ts +++ b/app/_lib/toolkit-data.ts @@ -104,7 +104,11 @@ export const readToolkitFile = async ( ); } - return result.data; + // Zod object schemas strip unknown keys from their parsed result. Return the + // original validated object so a newly added generator field is preserved by + // the app until the shared schema is updated, rather than silently deleting + // it from API responses or page data. + return parsed as ToolkitData; }; /** @@ -121,7 +125,7 @@ type ToolkitDataMap = { }; /** - * One process-wide load per data directory. Keyed by directory (not a single + * One process-wide load per data directory in production. Keyed by directory (not a single * flat variable) because tests point `TOOLKIT_DATA_DIR` at scratch fixtures * and must not see another test's cached data. * @@ -129,7 +133,9 @@ type ToolkitDataMap = { * rather than retried: the underlying files are static build output that * only change on a new deploy, so a bad file stays bad for the rest of this * process's life, and re-scanning 21.6 MB on every subsequent lookup hoping - * it healed itself would only add cost without ever succeeding. + * it healed itself would only add cost without ever succeeding. Development + * is deliberately excluded from this cache: the generator can update JSON + * while `next dev` is running, and a refresh should see that new snapshot. */ const loadsByDataDir = new Map>(); @@ -187,6 +193,10 @@ const loadAllToolkitDataUncached = async ( */ export const loadAllToolkitData = cache( async (dataDir: string): Promise => { + if (process.env.NODE_ENV === "development") { + return await loadAllToolkitDataUncached(dataDir); + } + let promise = loadsByDataDir.get(dataDir); if (!promise) { promise = loadAllToolkitDataUncached(dataDir); diff --git a/tests/toolkit-data-cache.test.ts b/tests/toolkit-data-cache.test.ts index 45d9ac618..e2676734e 100644 --- a/tests/toolkit-data-cache.test.ts +++ b/tests/toolkit-data-cache.test.ts @@ -1,7 +1,7 @@ -import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { afterAll, describe, expect, test } from "vitest"; +import { afterAll, describe, expect, test, vi } from "vitest"; import { readToolkitData } from "@/app/_lib/toolkit-data"; /** @@ -74,6 +74,32 @@ describe("readToolkitData against a clean fixture directory", () => { const data = await readToolkitData("no-such-toolkit-at-all", { dataDir }); expect(data).toBeNull(); }); + + test("development reads see regenerated files and preserve new fields", async () => { + vi.stubEnv("NODE_ENV", "development"); + + try { + await readToolkitData("ValidToolkitOne", { dataDir }); + writeFileSync( + join(dataDir, "validtoolkitone.json"), + JSON.stringify({ + ...JSON.parse( + readFileSync(join(dataDir, "validtoolkitone.json"), "utf8") + ), + label: "RegeneratedToolkit", + futureGeneratorField: "preserved", + }) + ); + + const data = await readToolkitData("ValidToolkitOne", { dataDir }); + expect(data?.label).toBe("RegeneratedToolkit"); + expect( + (data as unknown as Record).futureGeneratorField + ).toBe("preserved"); + } finally { + vi.unstubAllEnvs(); + } + }); }); describe("readToolkitData against a directory with one corrupt file", () => { diff --git a/tests/toolkit-generation-site.test.ts b/tests/toolkit-generation-site.test.ts new file mode 100644 index 000000000..440d0f66c --- /dev/null +++ b/tests/toolkit-generation-site.test.ts @@ -0,0 +1,81 @@ +import { mkdtemp, readFile, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, test } from "vitest"; +import { readToolkitData, readToolkitIndex } from "@/app/_lib/toolkit-data"; +import { listToolkitRoutes } from "@/app/_lib/toolkit-static-params"; +import { createJsonGenerator } from "@/toolkit-docs-generator/src/generator/json-generator"; +import type { MergedToolkit } from "@/toolkit-docs-generator/src/shared/toolkit-schemas"; + +const temporaryDirectories: string[] = []; + +afterEach(async () => { + await Promise.all( + temporaryDirectories + .splice(0) + .map((directory) => rm(directory, { recursive: true, force: true })) + ); +}); + +const fixtureToolkit: MergedToolkit = { + id: "FixtureApi", + label: "Fixture API", + version: "1.0.0", + description: "A generated fixture toolkit.", + metadata: { + category: "development", + iconUrl: "https://example.com/fixture.svg", + isBYOC: false, + isPro: false, + type: "arcade_starter", + docsLink: + "https://docs.arcade.dev/en/resources/integrations/development/fixture-api", + isComingSoon: false, + isHidden: false, + }, + auth: null, + tools: [], + documentationChunks: [], + customImports: [], + subPages: [], +}; + +describe("generated toolkit output through the docs app", () => { + test("generation, index loading, data loading, and route enumeration agree", async () => { + const dataDir = await mkdtemp(join(tmpdir(), "toolkit-generation-site-")); + temporaryDirectories.push(dataDir); + + const generator = createJsonGenerator({ + outputDir: dataDir, + generateIndex: true, + }); + const result = await generator.generateAll([fixtureToolkit]); + + expect(result.errors).toEqual([]); + expect(result.filesWritten).toHaveLength(2); + + const index = await readToolkitIndex({ dataDir }); + const data = await readToolkitData("fixture-api", { dataDir }); + const routes = await listToolkitRoutes({ + dataDir, + toolkitsCatalog: [], + }); + + expect(index?.toolkits).toEqual([ + expect.objectContaining({ + id: "FixtureApi", + category: "development", + toolCount: 0, + }), + ]); + expect(data).toMatchObject(fixtureToolkit); + expect(routes).toEqual([ + { toolkitId: "fixture-api", category: "development" }, + ]); + + const generatedFile = JSON.parse( + await readFile(join(dataDir, "fixtureapi.json"), "utf8") + ) as MergedToolkit; + expect(generatedFile.id).toBe("FixtureApi"); + }); +}); diff --git a/toolkit-docs-generator/scripts/check-stale-summaries.ts b/toolkit-docs-generator/scripts/check-stale-summaries.ts index 18b06b830..404bc1fb8 100644 --- a/toolkit-docs-generator/scripts/check-stale-summaries.ts +++ b/toolkit-docs-generator/scripts/check-stale-summaries.ts @@ -39,27 +39,47 @@ const listToolkitFiles = (): string[] => { } }; +const readToolkit = (file: string): ToolkitShape | null => { + try { + return JSON.parse( + readFileSync(join(TOOLKITS_DIR, file), "utf8") + ) as ToolkitShape; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(`✗ Cannot parse ${file}: ${message}`); + return null; + } +}; + +const getStaleFinding = ( + file: string, + toolkit: ToolkitShape +): { file: string; id: string; reason: string } | null => { + if (toolkit.summaryStale !== true) { + return null; + } + + return { + file, + id: typeof toolkit.id === "string" ? toolkit.id : file, + reason: + typeof toolkit.summaryStaleReason === "string" + ? toolkit.summaryStaleReason + : "unknown", + }; +}; + const main = (): number => { const stale: Array<{ file: string; id: string; reason: string }> = []; for (const file of listToolkitFiles()) { - let toolkit: ToolkitShape; - try { - const raw = readFileSync(join(TOOLKITS_DIR, file), "utf8"); - toolkit = JSON.parse(raw) as ToolkitShape; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - console.error(`✗ Cannot parse ${file}: ${message}`); + const toolkit = readToolkit(file); + if (!toolkit) { return 1; } - if (toolkit.summaryStale === true) { - stale.push({ - file, - id: typeof toolkit.id === "string" ? toolkit.id : file, - reason: - typeof toolkit.summaryStaleReason === "string" - ? toolkit.summaryStaleReason - : "unknown", - }); + + const finding = getStaleFinding(file, toolkit); + if (finding) { + stale.push(finding); } } diff --git a/toolkit-docs-generator/src/cli/exclusion-cleanup.ts b/toolkit-docs-generator/src/cli/exclusion-cleanup.ts index 3426f9798..5cfe5b1e3 100644 --- a/toolkit-docs-generator/src/cli/exclusion-cleanup.ts +++ b/toolkit-docs-generator/src/cli/exclusion-cleanup.ts @@ -18,6 +18,38 @@ export interface CleanupExcludedToolkitOutputResult { warnings: string[]; } +const appendReadWarnings = ( + warnings: string[], + result: RebuildIndexResult, + verbose: boolean +): void => { + if (result.readErrors.length > 0) { + warnings.push( + `Index rebuild skipped ${result.readErrors.length} unreadable toolkit file(s).` + ); + if (verbose) { + warnings.push( + ...result.readErrors.map( + (error) => `Index rebuild read error: ${error}` + ) + ); + } + } + + if (result.readWarnings.length > 0) { + warnings.push( + `Index rebuild reported ${result.readWarnings.length} warning(s).` + ); + if (verbose) { + warnings.push( + ...result.readWarnings.map( + (warning) => `Index rebuild warning: ${warning}` + ) + ); + } + } +}; + export const cleanupExcludedToolkitOutput = async ( options: CleanupExcludedToolkitOutputOptions ): Promise => { @@ -38,27 +70,7 @@ export const cleanupExcludedToolkitOutput = async ( try { const rebuildResult = await options.generator.rebuildIndexFromOutput(); - if (rebuildResult.readErrors.length > 0) { - warnings.push( - `Index rebuild skipped ${rebuildResult.readErrors.length} unreadable toolkit file(s).` - ); - if (options.verbose) { - for (const error of rebuildResult.readErrors) { - warnings.push(`Index rebuild read error: ${error}`); - } - } - } - - if (rebuildResult.readWarnings.length > 0) { - warnings.push( - `Index rebuild reported ${rebuildResult.readWarnings.length} warning(s).` - ); - if (options.verbose) { - for (const warning of rebuildResult.readWarnings) { - warnings.push(`Index rebuild warning: ${warning}`); - } - } - } + appendReadWarnings(warnings, rebuildResult, options.verbose); } catch (error) { warnings.push( `Excluded toolkit files were deleted, but index rebuild failed: ${error instanceof Error ? error.message : String(error)}` diff --git a/toolkit-docs-generator/src/diff/previous-output.ts b/toolkit-docs-generator/src/diff/previous-output.ts index 043a7bb90..2b03416f7 100644 --- a/toolkit-docs-generator/src/diff/previous-output.ts +++ b/toolkit-docs-generator/src/diff/previous-output.ts @@ -259,6 +259,29 @@ type FallbackToolkitResult = { droppedParameterCount: number; }; +const getOptionalString = ( + record: Record, + key: string +): string | undefined => + typeof record[key] === "string" ? record[key] : undefined; + +const getFallbackCustomSections = ( + record: Record +): Pick< + MergedToolkit, + "documentationChunks" | "customImports" | "subPages" +> => ({ + documentationChunks: Array.isArray(record.documentationChunks) + ? (record.documentationChunks as MergedToolkit["documentationChunks"]) + : [], + customImports: Array.isArray(record.customImports) + ? (record.customImports as MergedToolkit["customImports"]) + : [], + subPages: Array.isArray(record.subPages) + ? (record.subPages as MergedToolkit["subPages"]) + : [], +}); + const buildFallbackToolkit = ( record: Record, fallbackId: string @@ -274,29 +297,12 @@ const buildFallbackToolkit = ( const metadataResult = MergedToolkitMetadataSchema.safeParse(record.metadata); const authResult = MergedToolkitAuthSchema.safeParse(record.auth); - const summary = - typeof record.summary === "string" ? record.summary : undefined; + const summary = getOptionalString(record, "summary"); const summaryStale = typeof record.summaryStale === "boolean" ? record.summaryStale : undefined; - const summaryStaleReason = - typeof record.summaryStaleReason === "string" - ? record.summaryStaleReason - : undefined; - const generatedAt = - typeof record.generatedAt === "string" ? record.generatedAt : undefined; - - // Carry forward custom sections even when strict schema parse fails. - // These are hand-authored fields — losing them silently on any schema - // mismatch would permanently wipe content on the next write. - const documentationChunks = Array.isArray(record.documentationChunks) - ? (record.documentationChunks as MergedToolkit["documentationChunks"]) - : []; - const customImports = Array.isArray(record.customImports) - ? (record.customImports as MergedToolkit["customImports"]) - : []; - const subPages = Array.isArray(record.subPages) - ? (record.subPages as MergedToolkit["subPages"]) - : []; + const summaryStaleReason = getOptionalString(record, "summaryStaleReason"); + const generatedAt = getOptionalString(record, "generatedAt"); + const customSections = getFallbackCustomSections(record); return { toolkit: { @@ -316,9 +322,7 @@ const buildFallbackToolkit = ( secretsInfo: [], documentationChunks: [], })), - documentationChunks, - customImports, - subPages, + ...customSections, ...(generatedAt ? { generatedAt } : {}), }, droppedToolCount,