v0.35.3.0 fix wave: extract_facts items + git --no-recurse-submodules placement#1053
Merged
Conversation
…chema Three duplicate inline mappers existed across the MCP surface: - src/mcp/tool-defs.ts (stdio MCP buildToolDefs) - src/commands/serve-http.ts:837 (live HTTP MCP tools/list) - src/core/minions/tools/brain-allowlist.ts:84 (subagent tool registry) Each had subtly different items propagation. The HTTP MCP variant dropped items entirely, leaving extract_facts.entity_hints broken for OAuth- authenticated remote agents even after a buildToolDefs-only patch. The subagent variant propagated one level of items but used the same shallow shape so nested arrays would silently drop. Extract a single recursive paramDefToSchema helper exported from src/mcp/tool-defs.ts and have all three mappers consume it. Closes the bug class at the architecture level instead of patching one site at a time. The helper copies type, description, enum, default, and recursively rebuilds items so array-of-arrays preserves inner shape. Key ordering (type, description, enum, default, items) matches the pre-v0.34 inline mappers so JSON.stringify output stays byte-stable for every existing operation that does not use nested arrays. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eet candidates
Two array fields shipped without the items property required by JSON
Schema. Strict-mode validators (Gemini Pro structured outputs, OpenAI
strict tool definitions) reject the entire schema when any type:'array'
lacks items. Downstream agents on those providers couldn't use
extract_facts or the x_handle_to_tweet resolver.
extract_facts.entity_hints — declared items: { type: 'string' } matching
the handler at src/core/operations.ts:2733 which already coerces the
runtime value to string[].
handle_to_tweet outputSchema.candidates — full XTweetCandidate spec
including required + additionalProperties: false. The XTweetCandidate
TypeScript interface declares all five fields as required; without
required in the JSON Schema, a validator would accept {} as a valid
candidate. additionalProperties: false closes the OpenAI strict-mode
contract.
19 community PRs (#1028 #999 #980 #979 #910 #904 #847 #832 #863 #862
#812 for entity_hints; #910 caught candidates) converged on these
locations. This wave cherry-picks the deepest variant (#910 surfaced
both bugs) and centralizes via the paramDefToSchema helper from the
preceding commit so the live HTTP MCP tools/list path is also fixed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: DmitryBMsk (PR #910)
Git CLI accepts two flag positions:
git [global -c flags] <subcommand> [subcommand flags] [args]
Global -c config flags belong before the verb. Subcommand-specific
flags (like --no-recurse-submodules) belong after. Pre-v0.34
GIT_SSRF_FLAGS spliced both kinds before the verb, so cloneRepo
invoked:
git -c http.followRedirects=false ... --no-recurse-submodules clone URL DIR
Real git rejects this with exit 129 ("unknown option:
--no-recurse-submodules") because --no-recurse-submodules is a clone
subcommand flag, not a global config flag. Every remote-source clone
broke in production from v0.28 onward. The fake-git harness in
test/git-remote.test.ts exits 0 regardless of argv shape, which is
why CI never caught it.
Split GIT_SSRF_FLAGS (3 -c config flags, spread BEFORE the verb) from
GIT_SSRF_SUBCOMMAND_FLAGS (--no-recurse-submodules, spread AFTER the
verb). cloneRepo and pullRepo both spread the new constant after
their respective verbs. The constant names signal the position rule
so future additions land in the right place.
7 community PRs converged on this location (#1023 #1020 #985 #963
#846 #842 — #800 doesn't exist). This wave cherry-picks the semantic-
constant approach from #846's GIT_SSRF_SUBCOMMAND_FLAGS name (the
clearest signal of the position rule).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… guards Three new tests / test groups close the bug classes the wave fixes: test/mcp-tool-defs.test.ts — recursive structural guard walks every operation's inputSchema and fails with a property path if any type:'array' lacks items.type. Explicit fixture assertions for extract_facts.entity_hints.items.type and a synthetic nested-array ParamDef pinning items.items.type recursion. Without the explicit fixtures the legacyInlineMap byte-equality test is mirror-theater — mirroring both sides of the equality preserves the blind spot. test/git-remote.test.ts — split snapshot test into GIT_SSRF_FLAGS (3 global -c entries) and GIT_SSRF_SUBCOMMAND_FLAGS (--no-recurse-submodules). cloneRepo + pullRepo argv tests now assert the subcommand flag appears AFTER the verb index. Pre-v0.34 the pinned argv slice prefix included --no-recurse-submodules, which baked the bug into the test suite (codex catch). test/resolvers.test.ts — recursive walk over both inputSchema AND outputSchema for builtin resolvers (xHandleToTweetResolver, urlReachableResolver). Explicit imports rather than getDefaultRegistry(), which starts empty until commands/resolvers.ts runs — codex catch on a hollow-walk failure mode. Dedicated case pins candidates items shape including required + additionalProperties. Reference legacyInlineMap in mcp-tool-defs.test.ts mirrors the new recursive paramDefToSchema helper. No current op uses nested arrays so the byte-equality test stays green for every existing operation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The first rerank call of a CI run hits ZeroEntropy's cold-start latency (observed ~5-6s on Tier 2 LLM Skills runners; subsequent calls < 500ms). Two timeouts fired simultaneously at ~5s: 1. bun:test's default 5000ms per-test timeout caused (fail). 2. gateway.rerank's DEFAULT_RERANK_TIMEOUT_MS = 5000 fired right after, reported as "Unhandled error between tests". The next rerank test (top_n=2) ran in 409ms because the API was already warm. Cold-start is the only issue. Pass explicit timeoutMs to each rerank() call and a longer per-test timeout (30s) on both ZE rerank tests. Production DEFAULT_RERANK_TIMEOUT_MS stays at 5s for the search hot path — these E2E tests bypass it locally without changing the default that protects user latency. Unrelated to the fix-wave in this PR (mcp-tool-defs + git-remote + resolver guards). Lands here to keep Tier 2 LLM Skills green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update CLAUDE.md Key files annotations for the v0.35.2.0 fix wave: - src/mcp/tool-defs.ts: document new exported recursive paramDefToSchema helper and the three-consumer centralization (stdio MCP, HTTP MCP tools/list, subagent registry). - src/core/minions/tools/brain-allowlist.ts: paramsToInputSchema now consumes the shared helper. - src/commands/serve-http.ts: tools/list handler now consumes the shared helper (closes the HTTP MCP items-dropped bug class). - src/core/git-remote.ts: new entry. Documents the GIT_SSRF_FLAGS (global config, pre-verb) vs GIT_SSRF_SUBCOMMAND_FLAGS (subcommand-scoped, post-verb) split, the 7-month silent regression, and the position-anchored regression guard in test/git-remote.test.ts. Regenerated llms-full.txt to match. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # VERSION # package.json
Queue moved while this PR was open — v0.35.2.0 was claimed by master's v0.35.1.0 sibling work. Advancing one slot. No code changes; only: - VERSION + package.json: 0.35.2.0 → 0.35.3.0 - CHANGELOG.md: rewritten header + inline references - CLAUDE.md: rewritten 4 key-file annotations - llms-full.txt + llms.txt: regenerated to mirror CLAUDE.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # VERSION # package.json
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.
Summary
Fix wave: 19 stale community PRs land as one bisect-friendly PR with the architectural fix none of them surfaced.
Two real bugs in shipping code on master since v0.28+:
extract_facts.entity_hintsand X-API resolvercandidatesarray fields missingitems— JSON Schema strict-mode validators (Gemini Pro structured outputs, OpenAI strict tool defs) rejecttype: 'array'withoutitems. ~12 community PRs converged on entity_hints; only @DmitryBMsk's fix: add missing items field to two array schemas (Gemini Pro strict JSON Schema compat) #910 caughtcandidates.--no-recurse-submodulesspliced before the subcommand verb — realgitexits 129 withunknown option. Every remote-source clone/pull broken since v0.28. The fake-git test harness exits 0 regardless of argv shape, so CI never caught it. ~7 community PRs flagged this.Codex outside-voice review on top of the 19 community PRs surfaced the structural finding none of them saw: three duplicate
ParamDef → JSON Schemamappers existed across the MCP surface (stdio MCP, HTTP MCPtools/list, subagent brain-tool registry). The live HTTP MCP path atserve-http.ts:837and the subagent registry atbrain-allowlist.ts:84would BOTH have stayed broken after abuildToolDefs-only patch.This PR centralizes via one shared
paramDefToSchema(p: ParamDef)helper consumed by all three call sites — closes the bug class at the architecture level. Plus structural CI guards so it can't drift again silently.Test Coverage
Pre-Landing Review
Run via
/plan-eng-review+/codex review planin this session. CLEAR after absorbing codex findings.Eval Results
No prompt-related files changed — evals skipped.
Plan Completion
All 11 implementation tasks DONE. Plan file:
~/.claude/plans/system-instruction-you-are-working-zany-reddy.md.Verification Results
bun run typecheck— greenbun run verify— green (typecheck + 8 shell pre-checks)bun run test— 6565/0/0 (parallel + serial loop, 221s)bun test test/mcp-tool-defs.test.ts test/git-remote.test.ts test/resolvers.test.ts test/agent-cli.test.ts test/oauth.test.ts test/http-transport.test.ts— 223/0/0de73cbc8).TODOS
No TODOS.md items closed by this wave. Existing v0.34.x/v0.35.x follow-ups preserved verbatim.
Docs sync
CLAUDE.md— updated annotations forsrc/mcp/tool-defs.ts(new exported recursiveparamDefToSchemahelper, three-consumer centralization),src/core/minions/tools/brain-allowlist.ts(paramsToInputSchema now consumes the shared helper),src/commands/serve-http.ts(HTTP MCPtools/listhandler now consumes the shared helper, closes the items-dropped bug class). Added a new entry forsrc/core/git-remote.tsdocumenting theGIT_SSRF_FLAGSvsGIT_SSRF_SUBCOMMAND_FLAGSsplit and the position-anchored regression guard.llms-full.txt— regenerated to match.README.md,TODOS.md— no references to changed surfaces, no edits.CHANGELOG.md— canonical v0.35.2.0 entry written by /ship.Attribution
Bug locations cherry-picked from community PRs:
entity_hintsANDcandidates.GIT_SSRF_SUBCOMMAND_FLAGSsemantic split).serve-http.ts:837duplicate mapper, the third duplicate atbrain-allowlist.ts:84, the hollowgetDefaultRegistry()walk, the missingrequiredarray, and the verification-command typos — findings none of the 19 community PRs caught.Superseded PRs being closed with thank-you notes after merge: #1028, #1023, #1020, #999, #985, #980, #979, #963, #904, #863, #862, #847, #846, #842, #832, #812 (16 total; #910 contribution incorporated, #800 doesn't exist).
Test plan
bun run typecheckgreenbun run verifygreen (typecheck + 8 shell pre-checks)bun run testgreen (6565/0/0 across parallel + serial)bun -e "import('./src/core/git-remote.ts').then(m => m.cloneRepo('https://github.com/octocat/Hello-World.git', '/tmp/rg-smoke'))"— should succeed where pre-fix exited 129feedback_pr_wave_workflow.md🤖 Generated with Claude Code