Skip to content

fix(generation): guarantee valid JSON + expose structuredData for schema requests - #1080

Merged
murdore merged 1 commit into
releasefrom
feat/json-fix
Jun 11, 2026
Merged

murdore merged 1 commit into
releasefrom
feat/json-fix

Conversation

@murdore

@murdore murdore commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

This PR fixes two related JSON-validity problems in generate({ schema }), entirely within the NeuroLink SDK (nothing on the curator side). Verified live across providers with tools active and complex Zod schemas.


Commit 1 — structured output wrongly disabled for Vertex+Claude

TARA's production config is Vertex + claude-sonnet-4-6 + tools, and NeuroLink was disabling structured-output enforcement for the entire Vertex provider whenever tools were active. But the tools↔schema conflict is a Gemini-only API limitation; Vertex+Claude supports both at once. The over-broad gate forced Vertex+Claude into text mode where the model hand-wrote JSON and a single mis-escaped character broke JSON.parse.

Fix: structuredOutputPolicy.ts gates the exclusion on isGeminiProvider (Gemini only). A provider-agnostic coercion at the neurolink.ts SDK boundary guarantees content is valid JSON and exposes a parsed structuredData object for every provider (override providers like Vertex/Anthropic/Bedrock bypass GenerationHandler, so the guarantee lives at the boundary). Runtime conflicts (e.g. Groq) are detected via isToolsSchemaConflictError and retried without structured output.


Commit 2 — huge text silently truncated mid-JSON

The dominant real-world failure for large TARA responses. The native Claude paths hard-coded max_tokens to 4096, bypassing the 64K provider default. Past ~16 KB the JSON was cut mid-stream; the AI SDK skips parseCompleteOutput on finishReason="length", so the path fell to text-mode coercion, which closed the dangling JSON into a valid-but-incomplete object with no signal (the Vertex native path didn't even surface finishReason).

Fix (all in NeuroLink):

  1. resolveClaudeMaxTokens() — model-aware output ceiling (Sonnet 4.x → 64K, Opus 4.x → 32K, older models at their published limits); clamps over-large caller values so the native paths never 400. Applied at both Vertex+Claude and Anthropic native sites.
  2. Surface finishReason on the Vertex native generate path (Anthropic max_tokens → "length").
  3. Observable truncation — coerceJsonToSchema returns { repaired, truncated }; GenerateResult exposes jsonRepaired / jsonTruncated (set on finishReason="length" or an unclosed span) plus a WARN. No more silent data loss.
  4. Anthropic non-streaming — set a client-level timeout so the SDK's "streaming is required for long requests" pre-flight guard doesn't reject a large max_tokens, and scale the generate timeout when a large output budget is in play (the abort signal stays the real bound).

Verification (live)

Matrix with tools active + complex schemas (escaping torture, deeply-nested, array-heavy, huge-output 200-line script with no maxTokens) across Vertex (Claude Sonnet/Opus 4.6 + Gemini 2.5), direct Anthropic (Sonnet/Opus 4.6), Google AI Studio, OpenAI, plus breadth providers, plus dedicated complete-output and forced-truncation-is-observable tests.

  • Huge-output returns complete valid JSON (16–24 KB) where the old 4096 cap truncated it.
  • Forced truncation → still valid JSON and jsonTruncated=true, finishReason=length (observable, never silent).
  • Unit suite (no API) covers the policy predicate, balanced extractor, coercion, and the new repaired/truncated flags.
  • tsc clean · ESLint clean.

Guarantee vs. limitation (honest)

  • Guaranteed: for any generate({ schema }), content is valid JSON and structuredData is the parsed object — across every provider.
  • Model-dependent: schema shape conformance (a weak breadth model may emit valid-but-non-conforming JSON — a model limitation, not an SDK break).
  • Out of scope (flagged): OpenAI's provider-level 128K default exceeds smaller models' completion limits (e.g. gpt-4o-mini 16384) and 400s when a caller omits maxTokens — a pre-existing non-Claude issue for a separate follow-up.

Summary by CodeRabbit

  • New Features

    • Generation results now include structuredData plus jsonRepaired/jsonTruncated flags; text outputs are auto-coerced/repaired to valid JSON when a schema is requested.
  • Behavior / Fixes

    • Provider-aware rules refine when tools + structured output are disabled (with an explicit exception for certain Claude paths) and automatically retry without structured output on conflicts.
    • Model-aware max-token handling and adaptive request timeouts reduce unexpected truncation and surface truncation reasons.
  • Tests

    • Added deterministic and live JSON-validity test suites and package scripts.
  • Documentation

    • New docs/plan covering JSON validity, huge-text/truncation guidance.
  • Chores

    • Added a JSON repair dependency and test scripts.

Copilot AI review requested due to automatic review settings June 10, 2026 21:41
@vercel

vercel Bot commented Jun 10, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
neurolink Ready Ready Preview, Comment Jun 11, 2026 9:32pm

@github-actions

github-actions Bot commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Single Commit Policy - COMPLIANT

Status: Policy requirements met • 1 commit • Valid format • Ready for merge

📊 View validation details

📝 Commit Details

  • Hash: 6cafa3c0d987cf2ef4eae59e5c8e18292fbcfefa
  • Message: fix(generation): guarantee valid JSON for schema requests + fix huge-text truncation
  • Author: Sachin Sharma

✅ Validation Results

  • Single commit requirement met
  • No merge commits in branch
  • Semantic commit message format verified
  • Ready for squash merge to release branch

🤖 Automated validation by NeuroLink Single Commit Enforcement

@coderabbitai

coderabbitai Bot commented Jun 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Provider-aware tools/schema gating was added, balanced-brace JSON extraction and jsonrepair-backed coercion were implemented, GenerationHandler and neurolink were extended to surface parsed structuredData plus jsonRepaired/jsonTruncated flags, Claude token limits and timeouts were made model-aware, and unit + E2E JSON test suites and scripts were added.

Changes

JSON Validity for Structured Generation

Layer / File(s) Summary
Structured output policy & provider detection
src/lib/core/modules/structuredOutputPolicy.ts, test/continuous-test-suite-json.ts
New policy exports isGeminiProvider to identify Gemini/Vertex models, isToolsSchemaExclusionInForce to enforce tools↔schema exclusion only for Gemini providers when tools are enabled, and isToolsSchemaConflictError to detect runtime provider rejections. Unit tests validate classification and conflict-message recognition.
JSON extraction & coercion utilities
src/lib/utils/json/extract.ts, src/lib/utils/json/coerce.ts, src/lib/types/utilities.ts, test/continuous-test-suite-json.ts
Adds nextBalancedJsonSpan for quote/escape-aware balanced-span extraction and coerceJsonToSchema which builds candidate spans, attempts parse, falls back to jsonrepair, selects by optional Zod-like safeParse, and returns canonical content, structuredData, and repaired/truncated flags. Tests cover nested/escaped cases, repairs, truncation, and arrays.
Handler integration & result contracts
src/lib/core/modules/GenerationHandler.ts, src/lib/types/generate.ts, src/lib/neurolink.ts
GenerationHandler now uses isToolsSchemaExclusionInForce for gating, treats NoObjectGeneratedError and isToolsSchemaConflictError as retryable structured-output failures, coerces text-mode outputs into canonical JSON when needed, and exposes structuredData. Types and neurolink finalization propagate structuredData, jsonRepaired, and jsonTruncated.
Claude token limits & provider timeouts
src/lib/utils/tokenLimits.ts, src/lib/providers/anthropic.ts, src/lib/providers/googleVertex.ts
Introduces getClaudeMaxOutputTokens / resolveClaudeMaxTokens to compute model-aware output ceilings; Anthropic client timeout constant and request-timeout scaling were added; Vertex maps Anthropic stop_reason === "max_tokens" to finishReason: "length" to surface truncation.
Test infrastructure, dependencies & E2E
package.json, test/continuous-test-suite-json-e2e.ts, test/continuous-test-suite-json.ts
Adds jsonrepair dependency and test:json / test:json-e2e scripts. New pure unit suite and live provider-matrix E2E suite validate content parseability, presence/equivalence of structuredData, schema conformance (strict vs logged), truncation observability, and infra-error skipping; includes Vertex+Claude huge-text tests.
Specification & implementation plan
CLAUDE.md, docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
CLAUDE guidance clarified for Gemini-only gating, runtime conflict detection/retry, and generate({ schema }) JSON/structuredData guarantee; a detailed implementation plan with tasks and a Phase 2 truncation follow-up was added.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • juspay/neurolink#1075: Overlaps provider-side Anthropic/Claude request and streaming changes related to timeout/max_tokens handling.

Suggested labels

released

Suggested reviewers

  • Tara-ag
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(generation): guarantee valid JSON + expose structuredData for schema requests' accurately describes the main change: ensuring JSON validity for schema-based generation and exposing structured data, which is the core objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/json-fix

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

@github-actions

github-actions Bot commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

Documentation Validation Results

🚀 Documentation validation passed!

Check Status Result
Frontmatter Validation ✅ Passed
TypeScript Check ✅ Passed
Build ✅ Passed
Link Validation ✅ Passed

📦 Build artifact uploaded successfully. Ready for deployment preview.

Commit: aea16ec527c8345c123ace9838a471f88b993787 | Workflow: View logs

Copilot AI 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.

Pull request overview

This PR tightens structured-output gating so Vertex+Claude can use tools and JSON-schema enforcement together, and adds a provider-agnostic JSON “coercion” layer so generate({ schema }) returns syntactically valid JSON plus an exposed parsed structuredData object across providers.

Changes:

  • Introduces a Gemini-only tools↔schema exclusion policy (and runtime conflict detection) to avoid disabling structured output for Vertex+Claude.
  • Adds balanced-brace JSON extraction and jsonrepair-backed coercion to canonical JSON for text-mode/provider override paths.
  • Threads structuredData through generation results and adds unit + live e2e test suites for JSON validity.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/continuous-test-suite-json.ts Deterministic unit tests for policy predicate, extractor, and coercion behavior.
test/continuous-test-suite-json-e2e.ts Live cross-provider matrix validating content JSON-parses and structuredData matches.
src/lib/utils/json/extract.ts Replaces regex JSON extraction with quote/escape-aware balanced span scanning.
src/lib/utils/json/coerce.ts Adds jsonrepair-backed text→canonical-JSON coercion helper.
src/lib/types/utilities.ts Adds JsonCoercionResult type.
src/lib/types/generate.ts Exposes structuredData?: unknown on GenerateResult and TextGenerationResult.
src/lib/neurolink.ts Adds DTO-boundary coercion attempt + forwards structuredData to GenerateResult.
src/lib/core/modules/structuredOutputPolicy.ts New Gemini-only structured output exclusion + conflict detector helpers.
src/lib/core/modules/GenerationHandler.ts Uses new policy, retries on tools↔schema conflicts, and propagates/coerces structured data.
package.json Adds jsonrepair dependency and new JSON test scripts.
pnpm-lock.yaml Locks jsonrepair@3.14.0.
docs/superpowers/plans/2026-06-11-neurolink-json-validity.md Adds an implementation plan document for the JSON validity work.
CLAUDE.md Updates documented rules around Gemini-only tools↔schema limitation and JSON guarantees.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/utils/json/coerce.ts Outdated
Comment on lines +86 to +93
const firstBrace = text.indexOf("{");
const lastBrace = text.lastIndexOf("}");
if (firstBrace >= 0 && lastBrace > firstBrace) {
candidates.push(text.slice(firstBrace, lastBrace + 1));
}
if (firstBrace >= 0) {
candidates.push(text.slice(firstBrace));
}
Comment thread src/lib/neurolink.ts Outdated
Comment on lines +4509 to +4513
// Provider-agnostic JSON guarantee: when a schema was requested, ensure
// `content` is valid JSON conforming to it and expose the parsed object as
// `structuredData`. Providers that already produced structuredData (AI-SDK
// experimental_output) pass through untouched; every other provider path —
// including those that override generate() (Vertex, Anthropic, Bedrock,
Comment thread src/lib/core/modules/GenerationHandler.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/neurolink.ts (1)

4471-4534: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Emit generation:end/response:end only after schema coercion.

Line 4481 and Line 4503 emit pre-coercion textResult, but Line 4521+ mutates content/structuredData. Event consumers can observe stale/invalid JSON while generate() returns the coerced value.

Suggested fix
-    if (!nativeAlreadyEmitted) {
-      this.emitter.emit("generation:end", {
-        provider: textResult.provider,
-        responseTime: Date.now() - startTime,
-        toolsUsed: textResult.toolsUsed,
-        timestamp: Date.now(),
-        result: textResult,
-        prompt:
-          originalPrompt ||
-          options.input?.text ||
-          (options as Record<string, unknown>).prompt,
-        temperature: textOptions.temperature,
-        maxTokens: textOptions.maxTokens,
-        pipelineAHandled: true,
-      });
-    }
-    this.emitter.emit("response:end", textResult.content || "");
-    this.emitter.emit(
-      "message",
-      `Generation completed in ${Date.now() - startTime}ms`,
-    );
-
     if (
       textOptions.schema &&
       textResult.structuredData === undefined &&
       typeof textResult.content === "string"
@@
       }
     }
+
+    if (!nativeAlreadyEmitted) {
+      this.emitter.emit("generation:end", {
+        provider: textResult.provider,
+        responseTime: Date.now() - startTime,
+        toolsUsed: textResult.toolsUsed,
+        timestamp: Date.now(),
+        result: textResult,
+        prompt:
+          originalPrompt ||
+          options.input?.text ||
+          (options as Record<string, unknown>).prompt,
+        temperature: textOptions.temperature,
+        maxTokens: textOptions.maxTokens,
+        pipelineAHandled: true,
+      });
+    }
+    this.emitter.emit("response:end", textResult.content || "");
+    this.emitter.emit(
+      "message",
+      `Generation completed in ${Date.now() - startTime}ms`,
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/neurolink.ts` around lines 4471 - 4534, The events "generation:end"
and "response:end" are emitted before the schema coercion mutates
textResult.content/structuredData, causing consumers to see stale data; move the
emission of this.emitter.emit("generation:end", ...) (respecting the
nativeAlreadyEmitted guard and pipelineAHandled flag) and
this.emitter.emit("response:end", ...) to after the coerceJsonToSchema(...)
block and after textResult is updated so emitted payloads reflect the coerced
content/structuredData returned in generateResult.
🧹 Nitpick comments (2)
docs/superpowers/plans/2026-06-11-neurolink-json-validity.md (1)

1086-1108: ⚡ Quick win

Self-review missed the type definition location violation.

The self-review checklist is thorough and covers spec coverage, placeholder scanning, and type consistency. However, it doesn't catch that CoercionResult (mentioned in line 1099) violates coding guideline rule 2 by being defined locally in coerce.ts instead of in src/lib/types/utilities.ts.

Consider adding a guidelines compliance check to the self-review section:

  • All type definitions in src/lib/types/ per rule 2
  • All internal type imports from barrel per rule 13
  • No interface usage per rule 7
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-06-11-neurolink-json-validity.md` around lines
1086 - 1108, CoercionResult is defined locally in coerce.ts which violates rule
2; move the CoercionResult type definition from coerce.ts into the shared types
barrel (add it to src/lib/types/utilities.ts) and update coerce.ts to import
CoercionResult from the barrel; also update any other files referencing the
local type to import from src/lib/types/utilities.ts and ensure the barrel
export is added so internal imports follow rule 13.
test/continuous-test-suite-json-e2e.ts (1)

230-235: 💤 Low value

Consider breaking the long regex into multiple patterns for maintainability.

The comprehensive error-detection regex spans 230+ characters on a single line. While functionally correct, breaking it into an array of patterns or adding inline comments would improve readability and future maintenance.

♻️ Optional refactor for readability
 function isInfraError(message: string): boolean {
-  return /api key|apikey|credential|security token|unauthor|permission|quota|rate.?limit|too many requests|429|not found|unknown model|model.*not|region|ENOTFOUND|ECONNREFUSED|ECONNRESET|socket hang|fetch failed|network|timeout|deadline|unavailable|overloaded|throttl|capacity|exhausted|billing|credits|insufficient|payment|402|access|forbidden|invalid.*model|does not exist|status [45]\d\d|bad request|internal server|service unavailable/i.test(
-    message,
-  );
+  const patterns = [
+    /api key|apikey|credential|security token|unauthor|permission/i,
+    /quota|rate.?limit|too many requests|429|throttl/i,
+    /not found|unknown model|model.*not|does not exist|invalid.*model/i,
+    /region|ENOTFOUND|ECONNREFUSED|ECONNRESET|socket hang|fetch failed|network/i,
+    /timeout|deadline|unavailable|overloaded|service unavailable/i,
+    /capacity|exhausted|billing|credits|insufficient|payment|402/i,
+    /access|forbidden|status [45]\d\d|bad request|internal server/i,
+  ];
+  return patterns.some(pattern => pattern.test(message));
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/continuous-test-suite-json-e2e.ts` around lines 230 - 235, The long
regex in isInfraError makes maintenance hard; replace the single giant pattern
with a collection of smaller, descriptive patterns (e.g., an array named
INFRA_ERROR_PATTERNS) and update isInfraError to test the message against them
(either by joining them into a single RegExp with the 'i' flag or by iterating
and testing each pattern individually), keeping the original anchors/flags and
ensuring matching behavior stays the same; include short descriptive comments
for groups of related patterns (e.g., auth, rate limit, network, billing) so
future edits are localized and readable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-06-11-neurolink-json-validity.md`:
- Around line 764-989: The review notes that the code in
GenerationHandler.formatEnhancedResult uses the wrong type name for the result
of coerceJsonToSchema; update the usage to the correct exported type
JsonCoercionResult (instead of CoercionResult) returned by coerceJsonToSchema,
and ensure the utilities type export matches that name; locate references to
coerceJsonToSchema and the variable receiving its return in formatEnhancedResult
and change the type annotation/alias to JsonCoercionResult (or update the
utilities export to export the expected name) so the import and usage are
consistent with the type declared in utilities.
- Around line 620-726: Move the local type alias CoercionResult out of coerce.ts
into the shared utilities types module as JsonCoercionResult and export it; then
import and use JsonCoercionResult in coerceJsonToSchema (replace the local type
declaration and any references to CoercionResult), ensuring the exported type is
{ content: string; structuredData: unknown } so coerceJsonToSchema,
parseOrRepair, and related symbols continue to type-check against the new
JsonCoercionResult.
- Around line 35-51: The local type CoercionResult in coerceJsonToSchema should
be moved to a shared exported type named JsonCoercionResult in the project’s
utilities types module and imported instead of defining it locally; create and
export JsonCoercionResult in the types utilities module, add it to the barrel
export, then replace the local CoercionResult declaration with an import of
JsonCoercionResult in the coerceJsonToSchema implementation and update any
references (e.g., in coerceJsonToSchema and callers) to use the shared
JsonCoercionResult type.

---

Outside diff comments:
In `@src/lib/neurolink.ts`:
- Around line 4471-4534: The events "generation:end" and "response:end" are
emitted before the schema coercion mutates textResult.content/structuredData,
causing consumers to see stale data; move the emission of
this.emitter.emit("generation:end", ...) (respecting the nativeAlreadyEmitted
guard and pipelineAHandled flag) and this.emitter.emit("response:end", ...) to
after the coerceJsonToSchema(...) block and after textResult is updated so
emitted payloads reflect the coerced content/structuredData returned in
generateResult.

---

Nitpick comments:
In `@docs/superpowers/plans/2026-06-11-neurolink-json-validity.md`:
- Around line 1086-1108: CoercionResult is defined locally in coerce.ts which
violates rule 2; move the CoercionResult type definition from coerce.ts into the
shared types barrel (add it to src/lib/types/utilities.ts) and update coerce.ts
to import CoercionResult from the barrel; also update any other files
referencing the local type to import from src/lib/types/utilities.ts and ensure
the barrel export is added so internal imports follow rule 13.

In `@test/continuous-test-suite-json-e2e.ts`:
- Around line 230-235: The long regex in isInfraError makes maintenance hard;
replace the single giant pattern with a collection of smaller, descriptive
patterns (e.g., an array named INFRA_ERROR_PATTERNS) and update isInfraError to
test the message against them (either by joining them into a single RegExp with
the 'i' flag or by iterating and testing each pattern individually), keeping the
original anchors/flags and ensuring matching behavior stays the same; include
short descriptive comments for groups of related patterns (e.g., auth, rate
limit, network, billing) so future edits are localized and readable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d69aa770-644b-4ef6-9068-9226dc89d0f2

📥 Commits

Reviewing files that changed from the base of the PR and between 7e9ef68 and ec70ca5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • CLAUDE.md
  • docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
  • package.json
  • src/lib/core/modules/GenerationHandler.ts
  • src/lib/core/modules/structuredOutputPolicy.ts
  • src/lib/neurolink.ts
  • src/lib/types/generate.ts
  • src/lib/types/utilities.ts
  • src/lib/utils/json/coerce.ts
  • src/lib/utils/json/extract.ts
  • test/continuous-test-suite-json-e2e.ts
  • test/continuous-test-suite-json.ts

Comment thread docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
Comment thread docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
Comment thread docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/types/generate.ts (1)

271-301: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the Google tools+schema JSDoc.

These blocks still tell callers that Vertex schema requests must always set disableTools: true, but this PR narrows the restriction to Gemini-only calls. That will now mislead Vertex+Claude consumers into disabling tools even though this change explicitly restores that combination.

Based on learnings: "Public SDK API must not break existing callers."

Also applies to: 324-337

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/types/generate.ts` around lines 271 - 301, The JSDoc block above the
generate/schema definitions incorrectly states Vertex requests must always set
disableTools:true; update the comment to say the restriction applies only to
Google Gemini (Gemini API) when combining function calling with structured
output — callers using Vertex with Claude or other Vertex models can use schemas
without disabling tools. Edit the Zod schema JSDoc (the block referencing
"Google Gemini Limitation", "disableTools", and examples around the generate
call and schema param) to mention Gemini-only limitation and adjust the example
text accordingly so references to provider:"vertex" do not imply a universal
disableTools requirement.

Source: Learnings

🧹 Nitpick comments (1)
test/continuous-test-suite-json-e2e.ts (1)

62-68: ⚡ Quick win

Move local type aliases to src/lib/types/ per repo TypeScript rules.

SchemaResult, SchemaCase, and Cell are defined in this test file. As per coding guidelines: **/*.ts: “All type definitions must go in src/lib/types/.”

Also applies to: 153-166, 215-228

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/continuous-test-suite-json-e2e.ts` around lines 62 - 68, Extract the
local type aliases SchemaResult, SchemaCase, and Cell from the test file into a
shared types module and import them back into the test: create a types file
exporting these interfaces/types (ensuring names and shapes match exactly),
replace the inline definitions in the test with imports of SchemaResult,
SchemaCase, and Cell, and update any references in the test to use the imported
symbols; ensure the new types are exported from the module so other tests can
reuse them and run the typechecker.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/providers/anthropic.ts`:
- Around line 1471-1483: The current logic in generateTimeoutMs (using
params.max_tokens, getTimeoutForOptions, createTimeoutController) forces a
5‑minute floor and can silently override an explicit caller timeout; change it
so we only apply the 5‑minute floor when the caller did NOT provide an explicit
timeout or abortSignal in options. Concretely, detect options.timeout or
options.abortSignal and, if present, use getTimeoutForOptions(options) (and
respect the provided abortSignal) without applying Math.max(..., 300_000); only
compute Math.max(getTimeoutForOptions(options), 300_000) and create the
timeoutController when neither options.timeout nor options.abortSignal were
supplied.

In `@src/lib/providers/googleVertex.ts`:
- Around line 4018-4021: The computed finishReason (mapping lastStopReason ===
"max_tokens" ? "length" : "stop") is not being forwarded to downstream
callbacks/events; find where onFinish is invoked and where the "generation:end"
event is emitted (these currently hardcode "stop") and replace the hardcoded
string with the computed finishReason variable (or recompute the same expression
if out of scope) so truncation is visible to onFinish consumers and
generation:end listeners; ensure the same finishReason is used consistently in
the response object construction (finishReason) and in the payload passed to
onFinish and event emitters.

In `@src/lib/utils/json/coerce.ts`:
- Around line 94-104: The fallback candidate generation in
src/lib/utils/json/coerce.ts (used by coerceJsonToSchema) only scans for object
braces and therefore misses root arrays or truncated/wrapped arrays; update the
candidate extraction logic to also detect square-bracket pairs (firstBracket =
text.indexOf("["), lastBracket = text.lastIndexOf("]")) and push full and
truncated array slices as candidates (mirroring the existing brace-based
pushes), and apply the same array-aware addition to the subsequent fallback
block (the region around lines 106-149) so truncated/prose-wrapped arrays become
repair candidates too.

In `@test/continuous-test-suite-json-e2e.ts`:
- Around line 373-394: The test softens SDK guarantees by converting
structuredData parity to a log for non-strict (breadth) cells and by parsing
JSON only conditionally; instead, always assert SDK behavior for generate({
schema }): in the block using sdPresent, sdConsistent, cell.strictSchema,
replace the console.log branch with the same assertions used for strictSchema so
that res.structuredData is present and equals parsed for all cells, and ensure
the JSON parsing around parsed (lines ~520-527) is unconditional so parsed is
always available for comparison; use the identifiers res.structuredData, parsed,
sdPresent, sdConsistent, and cell.strictSchema to locate and update the checks.
- Around line 333-334: The current infra-skip regex (the large
/.../i.test(message) returned) is too broad—specifically it includes the
substring "bad request" and the generic "status [45]\d\d" which can mask real
SDK request/construction regressions; update that regex by removing "bad
request" and replacing the generic "status [45]\d\d" token with an explicit list
of infra-only status codes (e.g., 429, 502, 503, 504) so only known transient
infra errors are skipped, leaving true 4xx client errors (like 400/401/403/404)
to fail the tests; apply this change to the regex literal used in the return
statement shown (the /api key|apikey|...|context length/i.test(message)
expression).

---

Outside diff comments:
In `@src/lib/types/generate.ts`:
- Around line 271-301: The JSDoc block above the generate/schema definitions
incorrectly states Vertex requests must always set disableTools:true; update the
comment to say the restriction applies only to Google Gemini (Gemini API) when
combining function calling with structured output — callers using Vertex with
Claude or other Vertex models can use schemas without disabling tools. Edit the
Zod schema JSDoc (the block referencing "Google Gemini Limitation",
"disableTools", and examples around the generate call and schema param) to
mention Gemini-only limitation and adjust the example text accordingly so
references to provider:"vertex" do not imply a universal disableTools
requirement.

---

Nitpick comments:
In `@test/continuous-test-suite-json-e2e.ts`:
- Around line 62-68: Extract the local type aliases SchemaResult, SchemaCase,
and Cell from the test file into a shared types module and import them back into
the test: create a types file exporting these interfaces/types (ensuring names
and shapes match exactly), replace the inline definitions in the test with
imports of SchemaResult, SchemaCase, and Cell, and update any references in the
test to use the imported symbols; ensure the new types are exported from the
module so other tests can reuse them and run the typechecker.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 45501e79-bf6d-4482-b3f4-8ce931195ba9

📥 Commits

Reviewing files that changed from the base of the PR and between ec70ca5 and 5c5d84e.

📒 Files selected for processing (12)
  • CLAUDE.md
  • docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
  • src/lib/core/modules/GenerationHandler.ts
  • src/lib/neurolink.ts
  • src/lib/providers/anthropic.ts
  • src/lib/providers/googleVertex.ts
  • src/lib/types/generate.ts
  • src/lib/types/utilities.ts
  • src/lib/utils/json/coerce.ts
  • src/lib/utils/tokenLimits.ts
  • test/continuous-test-suite-json-e2e.ts
  • test/continuous-test-suite-json.ts
✅ Files skipped from review due to trivial changes (2)
  • CLAUDE.md
  • docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/types/utilities.ts
  • src/lib/neurolink.ts

Comment thread src/lib/providers/anthropic.ts
Comment thread src/lib/providers/googleVertex.ts
Comment thread src/lib/utils/json/coerce.ts Outdated
Comment thread test/continuous-test-suite-json-e2e.ts Outdated
Comment thread test/continuous-test-suite-json-e2e.ts Outdated
@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

@murdore

murdore commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Review Feedback Addressed (Cycle 1)

All 11 actionable findings from the automated reviewers (Copilot ×3, CodeRabbit ×8) have been addressed — 10 fixed, 1 declined with rationale below.

Changes Made

  • src/lib/utils/json/coerce.ts — CodeRabbit + Copilot: root-array JSON never reached the fallback repair candidates. The first/last/truncated candidate extraction now considers [/] alongside {/}, so truncated or prose-wrapped root arrays are recovered (e.g. ["a","b" cut off). Two new unit tests cover truncated and prose-wrapped arrays.
  • src/lib/neurolink.ts — CodeRabbit: generation:end/response:end emitted pre-coercion (stale payloads). The schema-coercion + truncation blocks now run before the end-of-generation emits, so event consumers see the same coerced content/structuredData the caller receives. Copilot: overstated "JSON guarantee" comment. The contract comment now states the real behavior, a scalar-JSON-root fallback (JSON.parse) exposes structuredData for non-object roots, and an unrecoverable-prose case now logs a WARN instead of failing silently.
  • src/lib/core/modules/GenerationHandler.ts — Copilot: null-coercion fallback silently returned non-JSON. Same scalar fallback + WARN in coerceTextMode; comment updated to the honest contract.
  • src/lib/providers/anthropic.ts — CodeRabbit: 5-min timeout floor overrode explicit caller timeouts. The floor now applies only when the caller did not set an explicit timeout — an explicit value is a contract and is never silently extended.
  • src/lib/types/generate.ts — CodeRabbit: stale JSDoc said all-Vertex schema calls require disableTools: true. Both JSDoc blocks now state the exclusion is Gemini-only, that Vertex Claude supports tools + schema together, and that the SDK auto-falls back when the combination is impossible.
  • test/continuous-test-suite-json-e2e.ts — CodeRabbit: infra-skip regex too broad; SDK guarantees soft-downgraded. Removed bad request and generic status [45]xx from the skip regex (now only 429/500/502/503/504); structuredData presence + parity are hard-asserted for all cells; JSON.parse(res.content) in the forced-truncation test is now unconditional.
  • docs/superpowers/plans/2026-06-11-neurolink-json-validity.md — CodeRabbit ×2: plan snippets showed a local CoercionResult type. Snippets now import JsonCoercionResult from the types barrel (matching the shipped code, which already followed rule 2), and src/lib/types/utilities.ts was added to the File Structure list.

Declined (with rationale)

  • CodeRabbit: move test-local type aliases (SchemaResult, SchemaCase, Cell) into src/lib/types/. Rule 2 (neurolink/no-local-type-alias) is intentionally ESLint-scoped to src/lib/ — these are test-harness-only shapes, and exporting them through the public types barrel would pollute the SDK surface (and trip rule 9/10 constraints) for zero consumer benefit. ESLint passes on these files as-is.

Validation

  • TypeScript (tsc --noEmit --strict): ✅ 0 errors
  • ESLint: ✅ 0 errors
  • Unit suite (test:json, no API): ✅ 20/20 (includes 2 new root-array tests)
  • Build (pnpm run build): ✅
  • Live smoke (vertex Claude/Gemini + xai + deepseek, incl. huge-output + forced-truncation): ✅ see CI/below

Notes

  • The two Yama PR Review check failures are explicitly "infrastructure error, not a code verdict — re-run this job to retry" (per the job annotations) — not related to the diff.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

@murdore Thank you for the thorough Cycle 1 summary — the fixes are well-targeted and the rationale for declining the test-type-alias migration is sound (ESLint rule scoped to src/lib/, no SDK surface benefit). Triggering a fresh review now.

🐇 ✨

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-06-11-neurolink-json-validity.md`:
- Around line 691-730: The coerceJsonToSchema function currently returns {
content, structuredData } but docs also expect repaired and truncated; reconcile
by updating the function signature and return to include optional
boolean/strings for repaired and truncated (e.g., return { content,
structuredData, repaired?: string|null, truncated?: boolean }) or alternatively
update the docs/Phase 2 description to match the existing shape—pick one
consistent contract; modify the function (coerceJsonToSchema) to populate
repaired when parseOrRepair changed the text and truncated when
nextBalancedJsonSpan cut content, and ensure the other documentation section is
updated to match the chosen shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5ae7a5de-5f6e-4024-b67e-4173e299e9d5

📥 Commits

Reviewing files that changed from the base of the PR and between 5c5d84e and 93d7531.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • CLAUDE.md
  • docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
  • package.json
  • src/lib/core/modules/GenerationHandler.ts
  • src/lib/core/modules/structuredOutputPolicy.ts
  • src/lib/neurolink.ts
  • src/lib/providers/anthropic.ts
  • src/lib/providers/googleVertex.ts
  • src/lib/types/generate.ts
  • src/lib/types/utilities.ts
  • src/lib/utils/json/coerce.ts
  • src/lib/utils/json/extract.ts
  • src/lib/utils/tokenLimits.ts
  • test/continuous-test-suite-json-e2e.ts
  • test/continuous-test-suite-json.ts
✅ Files skipped from review due to trivial changes (1)
  • CLAUDE.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • package.json
  • src/lib/utils/json/extract.ts
  • src/lib/utils/tokenLimits.ts
  • src/lib/core/modules/structuredOutputPolicy.ts
  • src/lib/types/generate.ts
  • test/continuous-test-suite-json.ts
  • src/lib/neurolink.ts
  • src/lib/providers/anthropic.ts
  • src/lib/core/modules/GenerationHandler.ts
  • test/continuous-test-suite-json-e2e.ts

Comment thread docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
…text truncation

Two related JSON-validity fixes for generate({ schema }), entirely in the SDK.

1) Structured output was wrongly disabled for Vertex+Claude. The tools<->schema
exclusion is a Gemini-only API limitation, but the gate keyed on the whole
Vertex provider, forcing Vertex+Claude+tools (TARA's production config) into
text mode where hand-written JSON broke on a single mis-escaped character.

- structuredOutputPolicy.ts: exclusion gated on isGeminiProvider only;
  isToolsSchemaConflictError detects runtime rejections (e.g. Groq) and
  transparently retries without structured output.
- Provider-agnostic guarantee at the neurolink.ts boundary: content is always
  valid JSON (balanced-brace scan + jsonrepair) and the parsed object is
  exposed as result.structuredData -- override providers (vertex/anthropic/
  bedrock/google-ai) bypass GenerationHandler, so the guarantee lives at the
  SDK boundary.

2) Huge structured responses were silently truncated. The native Claude paths
hard-coded max_tokens to 4096; past ~16KB the JSON was cut mid-stream and
coercion closed it into a valid-but-incomplete object with no signal (the
Vertex native path didn't even surface finishReason).

- resolveClaudeMaxTokens(): model-aware output ceiling (Sonnet 4.x 64K, Opus
  4.x 32K, older models at their published limits); clamps over-large caller
  values so the native paths never 400.
- Vertex native generate surfaces finishReason (max_tokens -> "length").
- coerceJsonToSchema returns { repaired, truncated }; GenerateResult exposes
  jsonRepaired / jsonTruncated plus a WARN -- truncation observable, never silent.
- Anthropic client sets an explicit timeout so the SDK's non-streaming
  long-request guard doesn't reject a large max_tokens; generate timeout
  scales when a large output budget is in play.

Verified live with tools active and complex Zod schemas (escaping torture,
nested, array-heavy, 200-line huge-output with no maxTokens) across Vertex
(Claude Sonnet/Opus 4.6 + Gemini 2.5), direct Anthropic (Sonnet/Opus 4.6),
Google AI Studio, OpenAI and breadth providers: 33 passed / 0 failed.
Huge outputs return complete valid JSON (16-24KB) where the old cap truncated;
forced truncation yields jsonTruncated=true + finishReason=length. Deterministic
unit suite covers the policy predicate, extractor, coercion and the new flags.
@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-06-11-neurolink-json-validity.md`:
- Around line 19-29: Retitle the section header to indicate this is a pre-change
baseline and mark the listed bullets as historical context (e.g., prepend
"Pre-change baseline:"), and add a short note clarifying which specific facts
are outdated — namely the Vertex-wide gate behavior (GenerationHandler.ts
useStructuredOutput / isAnthropicProvider), the behavior of
formatEnhancedResult, the existence/handling of structuredData in
GenerateResult, and that jsonrepair is now a dependency — so readers won’t treat
the bullets (including options.schema and NoObjectGeneratedError mentions) as
current implementation facts.

In `@src/lib/utils/tokenLimits.ts`:
- Around line 158-161: The guard in the shown function treats requested===0 as
"not specified" (returns ceiling) which is inconsistent with getSafeMaxTokens
that preserves explicit 0; decide and make it explicit: either (A) preserve 0
like getSafeMaxTokens by removing the requested>0 check so requested===0 is
returned, or (B) treat 0 as invalid for Claude and keep the current behavior but
add an explicit check and comment (e.g., if (requested === 0) { /* Claude
requires max_tokens>0; treat 0 as unspecified */ } ) and add a short doc comment
to the function to document this choice; update the guard accordingly and ensure
getSafeMaxTokens and this function have consistent semantics and documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3a62a25b-c6aa-4e10-8e77-b80eb78c60be

📥 Commits

Reviewing files that changed from the base of the PR and between 93d7531 and 6cafa3c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • CLAUDE.md
  • docs/superpowers/plans/2026-06-11-neurolink-json-validity.md
  • package.json
  • src/lib/core/modules/GenerationHandler.ts
  • src/lib/core/modules/structuredOutputPolicy.ts
  • src/lib/neurolink.ts
  • src/lib/providers/anthropic.ts
  • src/lib/providers/googleVertex.ts
  • src/lib/types/generate.ts
  • src/lib/types/utilities.ts
  • src/lib/utils/json/coerce.ts
  • src/lib/utils/json/extract.ts
  • src/lib/utils/tokenLimits.ts
  • test/continuous-test-suite-json-e2e.ts
  • test/continuous-test-suite-json.ts
✅ Files skipped from review due to trivial changes (1)
  • CLAUDE.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/lib/types/utilities.ts
  • package.json
  • src/lib/providers/googleVertex.ts
  • src/lib/types/generate.ts
  • src/lib/utils/json/coerce.ts
  • test/continuous-test-suite-json.ts
  • src/lib/neurolink.ts
  • test/continuous-test-suite-json-e2e.ts
  • src/lib/core/modules/structuredOutputPolicy.ts
  • src/lib/providers/anthropic.ts
  • src/lib/core/modules/GenerationHandler.ts

Comment on lines +19 to +29
**Verified facts this plan relies on:**

- `GenerationHandler.ts` gate: `const useStructuredOutput = wantsStructuredOutput && !(isGoogleProvider && shouldUseTools && Object.keys(tools).length > 0);` where `isGoogleProvider = providerName === "google-ai" || providerName === "vertex"`.
- The file already defines (but does not use here) `isAnthropicProvider = ... || (providerName === "vertex" && modelName?.startsWith("claude-"))`.
- `formatEnhancedResult` sets `content = JSON.stringify(experimental_output)` when present, else strips fences from `generateResult.text` — and **discards** the parsed object.
- `NoObjectGeneratedError` fallback (re-runs without `experimental_output`) already exists → enabling structured output for Vertex+Claude is strictly safe.
- TARA runtime defaults: `neurolink-provider=vertex`, `neurolink-model=claude-sonnet-4-6`, tools registered (curator `registry.ts`).
- `GenerateResult` (src/lib/types/generate.ts) has no `structuredData` field; DTO builder in `neurolink.ts` (`const generateResult: GenerateResult = { content: textResult.content, ... }`) does not set one.
- `options.schema` type is `ValidationSchema = ZodTypeAny | Schema<unknown>` (Zod schema _or_ AI-SDK JSON schema).
- `jsonrepair` is NOT yet a dependency.
- Tests: `import { defineSuite, assert, assertEqual, assertNotNull } from "./helpers/harness.js"`, `const { test, runSuite } = defineSuite("…")`, run via `npx tsx test/<file>.ts`. `tsx` can import `src/**/*.ts` directly (fast TDD, no build).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Retitle this as pre-change baseline.

This block still says the old Vertex-wide gate is the verified fact and that jsonrepair is not yet a dependency, both of which are no longer true in the current implementation. That makes the section misleading; either refresh it or label it explicitly as historical baseline context.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-06-11-neurolink-json-validity.md` around lines 19
- 29, Retitle the section header to indicate this is a pre-change baseline and
mark the listed bullets as historical context (e.g., prepend "Pre-change
baseline:"), and add a short note clarifying which specific facts are outdated —
namely the Vertex-wide gate behavior (GenerationHandler.ts useStructuredOutput /
isAnthropicProvider), the behavior of formatEnhancedResult, the
existence/handling of structuredData in GenerateResult, and that jsonrepair is
now a dependency — so readers won’t treat the bullets (including options.schema
and NoObjectGeneratedError mentions) as current implementation facts.

Comment on lines +158 to +161
if (requested !== undefined && requested !== null && requested > 0) {
return Math.min(requested, ceiling);
}
return ceiling;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify or align 0-handling with getSafeMaxTokens.

Line 158's guard requested > 0 treats 0 as "not specified" and returns the ceiling, whereas getSafeMaxTokens (lines 45–49) preserves explicit 0 when provided. This inconsistency could surprise callers. Since the Claude API requires max_tokens > 0, returning the ceiling is safer than forwarding 0, but consider documenting this behavior or validating 0 explicitly to make the intent clear.

📝 Suggested documentation addition
 /**
  * Resolve the `max_tokens` to send on a native Anthropic/Claude request: honour
  * the caller's value but clamp it to the model's published ceiling, and default
- * to that ceiling when the caller did not specify one. Prevents both silent
- * truncation (the legacy 4096 default) and 400s from over-large requests.
+ * to that ceiling when the caller did not specify one (or passed 0 or a negative
+ * value, which are invalid for the Claude API). Prevents both silent truncation
+ * (the legacy 4096 default) and 400s from over-large requests.
  */
 export function resolveClaudeMaxTokens(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/utils/tokenLimits.ts` around lines 158 - 161, The guard in the shown
function treats requested===0 as "not specified" (returns ceiling) which is
inconsistent with getSafeMaxTokens that preserves explicit 0; decide and make it
explicit: either (A) preserve 0 like getSafeMaxTokens by removing the
requested>0 check so requested===0 is returned, or (B) treat 0 as invalid for
Claude and keep the current behavior but add an explicit check and comment
(e.g., if (requested === 0) { /* Claude requires max_tokens>0; treat 0 as
unspecified */ } ) and add a short doc comment to the function to document this
choice; update the guard accordingly and ensure getSafeMaxTokens and this
function have consistent semantics and documentation.

@murdore
murdore merged commit 7a79391 into release Jun 11, 2026
18 of 21 checks passed
@murdore
murdore deleted the feat/json-fix branch June 11, 2026 22:40
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 9.70.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

This branch was successfully deployed

1 active deployment
Preview — 6cafa3c0 Deployed Jun 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants