Harden registry-drift vocab: projected MCP schema, not Zod internals (v0.5.1, closes #27) - #33
Merged
Merged
Conversation
… internals (#27) vocabFromRegistry reached into Zod internals (verb.input._def.shape(), field._def.entries) inside a swallowing try/catch — a private surface a zod/verbspec bump could change, silently degrading the vocab to {help,version}. Since an unknown --flag is an error, a degraded vocab false-positives valid copy (--catalog, --store) and breaks the consumer gate. - prose.mjs: replace vocabFromRegistry(reg) with vocabFromToolset(toolset) — pure, builds from verbspec's PUBLIC projection ({ name, inputSchema }: name → verb id, inputSchema.properties → flags, properties[x].enum → enum values). No _def access. Add a fail-safe: registryDrift no-ops on a degraded vocab (flags.size <= 2) so projection failure → no findings, never false errors. - verbs.mjs: project via Object.values(registry).map(toMcpTool) (its inputSchema is the contract test.mjs already pins) and pass the built vocab in. - test.mjs: build the test vocab from a projected-toolset stub; add a regression guard that builds from the REAL registry — a bump that breaks projection fails CI here instead of silently disabling the gate — plus a degraded-vocab no-op. - README: refresh Status to v0.5.1 (was stale at v0.4.0); note registry-drift. - package.json: 0.5.0 → 0.5.1 (release workflow auto-cuts on merge). Closes #27. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011GNDVjvsbTPsbWV9fEyjLt
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.
Closes #27. Removes a latent false-positive risk in the registry-drift gate.
The bug
vocabFromRegistrybuilt the drift vocab by reaching into Zod internals —verb.input._def.shape(),field._def.entries— inside a swallowingtry/catch. A zod/verbspec bump that changes those private surfaces silently degrades the vocab to{help, version}. And because an unknown--flagis anerror, a degraded vocab turns valid copy (--catalog,--store) into false-positiveerrors that break the consumer gate — with thecatchhiding the failure.The fix
prose.mjs— replace it withvocabFromToolset(toolset): pure, builds from verbspec's public projection ({ name, inputSchema }—name→ verb id,inputSchema.propertieskeys → flags,properties[x].enum→ enum values). No_defaccess. Plus a fail-safe:registryDriftno-ops when the vocab is degraded (flags.size <= 2), so a projection failure yields no findings rather than false errors.prose.mjsstays verbspec-free (the consumer's prose-only gate keeps no JSR deps).verbs.mjs— projects viaObject.values(registry).map(toMcpTool)(itsinputSchemais the contracttest.mjsalready pins) and passes the built vocab in.test.mjs— test vocab now comes from a projected-toolset stub; adds a regression guard that builds from the REAL registry (toMcpTooloverregistry) — so a future bump that breaks projection fails CI here instead of silently disabling the gate — plus a degraded-vocab no-op assertion.Verify
Pure functions smoke-tested locally (vocab build, drift detection, degraded no-op, non-doc-type skip all pass). The real-registry projection test runs in CI under installed verbspec.
Release
Includes
package.json0.5.0 → 0.5.1(and refreshes the stale README Status, which still read v0.4.0). On merge, the release workflow auto-tags + publishes v0.5.1; the consumer can pin to it for the hardened gate.🤖 Generated with Claude Code
Generated by Claude Code