Repository navigation
feat(packs): behavior records with cited sources; zod's MCP input schema record (UPT-114) - #244
Merged
Merged
Conversation
…ema record (UPT-114) ADR 0017's record format as shared code (packs/records.ts): a silent behavior change is a finding only with a verbatim quote from a page whose snapshot and sha256 are checked in, and a site the type checker resolved. The first trigger, flows-to-consumer, follows a value of the package's type into another package's API; UPT-113 adds Stripe's on the same format. zod/mcp-input-schema-additional-properties: a zod 3 raw shape or plain object passed as inputSchema to McpServer#registerTool or #tool (@modelcontextprotocol/sdk >=1.23.0 <2) loses additionalProperties: false in the schema clients see on zod 4, citing zod's JSON Schema docs and the SDK 1.30.1 converter. The edit, .strict(), is not exact: it also makes the server reject unknown keys, so fix puts it under "Decisions for you" with where it was applied. fix tells the agent each record site with its quotes and the edit, re-matches the record on the branch to see which sites the edit reached, and gives the agent the target CHANGELOG.md lines (from the copy just installed) that name used symbols and say a behavior changed. Ground truth gains silent[] and pack test a "silent changes caught" tally that fails on any false positive; zod's truth gains consetto/calmcp (9 findings, 5 silent sites, all caught). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
… line order (UPT-114) From the calmcp bench run on e727cb4: the PR's risk said "unverified sites" for the record's five sites, which are cited behavior changes the owner decides; the record line listed them bottom-up; and the agent's own decision restated the record line. Risk now says "behavior changes", sites are in file and line order, and an agent decision naming a record the PR already decides is not said again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
… the packs tables (UPT-114) The fixture's stub `mcp.d.ts` sat under `sdk/dist/`, which the root .gitignore drops, so CI had no McpServer type and flows-to-consumer.test.ts found no site. The stub now lives under `sdk/types/`, which its package.json exports. `pnpm docs:packs` rewrites the README and compatibility tables for zod's new verification.json (calmcp: 224 breaking sites, 48% found as breaking, string-format 22 real true positives). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
lucasbraum
force-pushed
the
lucasbraum/upt-114-zod-mcp-behavior-records
branch
from
October 19, 2026 01:09
4d349fd to
1cb9ecc
Compare
A test that times out never reaches its `finally`, so its peerPreflight spy kept answering later tests' preflights: on PR #244's Node 20 run one 20 s timeout became four failures, three of them 'fixture peer blocker'. afterEach restores them, so a stalled runner fails one test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HgpwaD6vTJAGb2E8JpsGP9 Signed-off-by: Claude <noreply@anthropic.com>
Merged
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.
UPT-114, following ADR 0017: the behavior-record format as shared code, and the first record, for zod 3 → 4 behind the MCP SDK.
What changes
packs/records.ts). A record is a silent change with a cited source and a trigger that finds sites with the type checker (ADR §2). UPT-113 reuses the format for Stripe.sources. Each has a URL, a read date, a verbatimquote, and a checked-insnapshotof the page with itssha256.recordProblems()refuses a quote that isn't in its snapshot, a snapshot whose hash changed, or a non-exactkeepOldwith noalsoChanges. The pack contract test andpack testboth run it.trigger.flows-to-consumer(packs/flows-to-consumer.ts): a value of the package's type passed to another package's function. Receiver type, method, argument and property are data. The consumer's installed version must be in the record's range.effect,keepOld(edit,exact,alsoChanges),adopt.unverified, never breaking, and lists every quote and URL under the site.zod/mcp-input-schema-additional-properties(packs/zod/records.json,packs/zod/sources/).strip)z.object, passed asinputSchematoMcpServer#registerTool, or as a schema argument to#tool, with@modelcontextprotocol/sdk>=1.23.0 <2.any, another type'sregisterTool, and a workspace that importszod/v3.cc4cd4e: "When converting to JSON Schema in"input"mode,additionalPropertiesis not set."io: opts?.pipeStrategy ?? 'input'.z.strictObject()will always setadditionalProperties: false".741de4eit matches exactly the five lines the human changed:src/tools/index.ts:69, 83, 102, 119, 148.fix.Behavior changes at these sitesblock with each site, the quotes, the edit and what else it changes. It asks the agent to apply the edit, or to say in decisions why not, naming the record id.node_modules, so there's no new network use (only the target is extracted, and its changelog covers the range).default,no longer,now,instead oforsilently.silent[{ file, line, record, kind: 'required' | 'product-choice' }]. zod's ground truth gainsconsetto/calmcp: 9 breaking/deprecated findings frombdaef39and tsc, and the 5 required silent sites.pack test. It scores silent changes caught apart from breaking, overall and behavior notes. A record site on aproduct-choiceline, or on any line the truth doesn't name, is a false positive. Any silent FP failspack test(the zero-FP gate).verification.json. It getssilentonly for a pack with records or silent truth, so no other pack's record goes stale.339e306(fast-forward, nothing rewritten).selection.jsongains calmcp's fivesilentsites.score.mjscounts a site as caught when the diff touches its line and the head file has the keep edit, or when the PR body names the record.Behavior change (<record>)line names and the selection doesn't is a silent false positive.Decision for you:
.strict()also rejects unknown keysThe edit that keeps zod 3's advertised schema is
.strict()(z.object(shape).strict()for a raw shape). It isn't exact. On both majors calmcp's server silently dropped an unknown key; with.strict()the SDK rejects the call withInvalidParams, naming the key. The human chose that too (their testnames an invented parameter instead of dropping it). It's also a behavior change neither version had, so the record isexact: false. Its PR line always says what else the edit changes, and offers dropping it, which keepsadditionalPropertiesunset and unknown keys silently dropped.Should the record keep recommending
.strict(), or describe both options without recommending one?Measured
pack test zod, all 7 repositories, run fresh:calmcp alone: predicted 9, expected 9, 0 FP, 0 FN. Every other repository's numbers are unchanged.
Other verified packs.
pack teston all 12 others: ai, express, lucide-react, next, react-router, react, redis, @sentry/browser, @sentry/nextjs, stripe, typescript, vitest.staleonly because two keys undermechanicalcome out in a different order. The values are identical, and stripe is untouched here.Tests. core: 2321 passed. cli: 536 passed. biome: clean apart from one existing warning.
UpgradeBench calmcp rerun. One run, with the #169 harness (
bd96018plus the scorer commit).pnpm packofe727cb4, run asnpx -y file:uptide-0.16.1.tgz).claude-sonnet-5-5, effort medium, default--max-costof $1, an empty cache..strict()appliedz.object(shape).strict()at eachinputSchema)build (22)andbuild (24)fail atnpm audit --omit=dev --audit-level=highon an MCP SDK advisory (GHSA-6qxp-vccf-f47h) in the parent's lockfile.dependency-reviewcan't run because the fork has no dependency graph.53ca020, which changes only PR wording and was not rerun:Not in this PR
checkstill prints record sites as "+5 possible impact … not counted as breaking", and--detailslists them under UNVERIFIED with the quotes. A Behavior cell and a headline that count matched records are UPT-113 item 5.ponytail:infix/run.ts.run.shgot a local, uncommittedUPTIDE_SPEChook, as in the UPT-108 round, to run the packed tarball through npx.🤖 Generated with Claude Code