Skip to content

test: add the toolkit join parity harness - #1113

Open
teallarson wants to merge 5 commits into
mainfrom
chore/join-parity-harness
Open

test: add the toolkit join parity harness#1113
teallarson wants to merge 5 commits into
mainfrom
chore/join-parity-harness

Conversation

@teallarson

@teallarson teallarson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Why

The generator is being split into upstream data, enrichment, and curation layers. This adds a deterministic parity gate that answers one question: does joining those layers still reproduce the toolkit JSON we ship today?

It turns output preservation from a manual review task into a mechanical check, protecting the pipeline as its sources are extracted and reorganized.

How it fits

catalog snapshot + enrichment + curation
                    │
                    v
              real merger
                    │
                    v
          parity check vs shipped JSON

The harness sits alongside generation rather than replacing it: it uses the production merger, reports the first structural difference by JSON path, and makes future pipeline slices independently verifiable.

Changes

  • Added a paginated catalog snapshot script that records total_count and rejects truncated fetches.
  • Added a join verifier using the real merger, with committed toolkit data as the current baseline until extracted layers land.
  • Added tests for the zero-difference case, enrichment changes, missing catalog tools, and the catalog response shape.

Verification

  • Re-joining all 117 committed toolkits produces zero differences.
  • Injected enrichment changes fail with a precise JSON path.
  • Optional-field equivalence and volatile generatedAt are handled structurally to avoid false positives.

Independent of #1112 at merge time, but intended to validate it and the subsequent enrichment/source-extraction slices.


Note

Low Risk
New scripts and tests only; no changes to production merge or publish paths. Harness reads local JSON and optional Engine credentials for snapshot capture.

Overview
Adds a mechanical parity gate for the upcoming catalog / enrichment / curation split: join those inputs through the real mergeToolkit path and assert the result matches committed toolkit JSON under data/toolkits.

capture-catalog-snapshot.ts paginates Engine /v1/tool_metadata (same URL shape as production), records totalCount, and refuses to write if the fetch is truncated. Snapshots are gitignored (~10 MB).

verify-toolkit-join.ts loads a snapshot via parseToolMetadataResponse + groupToolsByToolkit, runs joinToolkit (merger + frozen enrichment overlay for codeExample / secretsInfo / summary), and compares structurally with firstDifference—ignoring generatedAt and treating optional null/absent keys as equivalent. Until separate layer dirs exist, enrichment and curation default to the reference toolkit files. Non-zero exit on any JSON-path mismatch or reference tool missing from the catalog.

Vitest covers the diff helper, round-trip parity across all committed toolkits (offline catalog projected from each file), drift detection, and API reshape fixtures.

Reviewed by Cursor Bugbot for commit e9a38de. Bugbot is set up for automated code reviews on this repo. Configure here.

Build the oracle S5 and S6 are judged by: does joining the extracted
layers (enrichment + curation) against a catalog snapshot reproduce the
toolkit JSON we ship today?

- capture-catalog-snapshot.ts pulls a raw /v1/tool_metadata snapshot
  (paginated at 1000), records total_count, and refuses to write a
  truncated fetch. The snapshot is gitignored (~10 MB, never committed).
- verify-toolkit-join.ts joins a snapshot with the enrichment and
  curation layers through the real merger (no LLM, committed toolkit as
  previousToolkit) and reports the first structural difference per file
  by JSON path. Comparison is structural, not raw-byte: generatedAt is
  volatile and summary's key position varies across generator versions.

Until S3/S5 land the enrichment/ and curation/ directories, the layers
are read straight out of the committed toolkit JSON (the default).

Proven offline: projecting every committed tool back to its pre-merge
shape and re-joining reproduces all 117 toolkits with zero differences;
an injected one-character enrichment change and a missing catalog item
each fail with a named JSON path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 7, 2026 5:34pm

Request Review

These scripts live in scripts/, which nothing type-checks today — #1106
widens the generator project to cover it. Under that project's
exactOptionalPropertyTypes and noUncheckedIndexedAccess they do not
compile, so #1106 landing would break this PR.

- IMetadataSource was imported from src/sources/interfaces, which does
  not export it; it lives in src/sources/internal, where all eight other
  consumers import it from. It is an 'import type', so it erased at
  runtime and no test could catch it.
- Bind the indexed argv reads before use: a truthiness check on
  argv[i + 1] does not narrow a later, separate read of the same index.
- Declare the two optional properties that legitimately receive an
  explicit undefined as '| undefined' rather than spreading conditionally
  at each call site.

No behaviour change; the harness still reproduces all 117 toolkits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@teallarson
teallarson marked this pull request as ready for review August 6, 2026 21:22
Comment thread toolkit-docs-generator/scripts/verify-toolkit-join.ts Outdated
Comment thread toolkit-docs-generator/scripts/verify-toolkit-join.ts
Comment thread toolkit-docs-generator/scripts/verify-toolkit-join.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e9a38de. Configure here.

total_count,
});
return groupToolsByToolkit(items);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verifier skips snapshot truncation check

Medium Severity

loadCatalogTools never checks that snapshot items.length matches the recorded totalCount/total_count. Capture comments say truncated fetches are detectable by the verifier, but a partial snapshot still parses and proceeds, so parity failures look like missing tools instead of a bad snapshot.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e9a38de. Configure here.

}
items.push(...page.items);
process.stdout.write(`\r fetched ${items.length}/${totalCount} tools`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Snapshot pagination skips short pages

Medium Severity

The capture loop advances offset by fixed PAGE_SIZE instead of page.items.length, unlike EngineApiSource which this script claims to mirror. A short non-final page skips tools and trips the truncation guard, so a catalog the generator can fetch may be impossible to snapshot.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e9a38de. Configure here.

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.

1 participant