Review Claude Code output against the task contract
Review a Claude Code API patch for validation, authorization, response semantics, and test independence before accepting the generated diff.

For a developer reviewing Claude Code output, evaluate the patch against the endpoint’s contract and the surrounding trust boundaries. A new validator can reject malformed input while accidentally allowing a caller to edit another user’s record. Passing tests are useful only when they exercise the behavior that changed and the boundaries that must remain intact. This lesson assumes you can read a JavaScript function and follow a request from its route to a stored record.
The Claude Code overview, retrieved on September 21, 2026, describes code inspection and editing capabilities. The workflow here is a review proposal, not a claim about a specific tool benchmark or an incident in Minh’s work.
Start with a narrow API task
Suppose a hypothetical profile endpoint accepts a display name. The requested repair is to reject blank names after trimming and preserve the current profile when validation fails. The caller may update only their own profile within their current tenant. This teaching contract allows one to eighty Unicode code points after trimming, and only the displayName input key. Those bounds are explicit product choices, not a universal rule for personal names. The endpoint returns a documented validation error that the client can display beside the field.
A plausible patch adds this check:
function trimOnly(body) {
const displayName = body.displayName.trim();
return displayName.length === 0
? { code: "display_name_required" }
: { displayName };
}
The snippet illustrates the intended validation but is not a complete safe handler. If displayName is missing or not a string, .trim() can throw. The rest of the handler must also derive the target profile from authenticated context or verify access to a supplied identifier.
The reviewer should therefore ask where runtime type validation occurs, where authorization occurs, and whether the update happens only after both succeed. Those questions are more useful than approving the snippet because it looks concise.
The figure treats validation and authorization as separate conditions before persistence. Their ordering can vary with the application’s information-disclosure policy, but neither condition should disappear because the task focused on the other.
Trace the actual request boundary
Read the route registration and middleware, not just the edited function. A schema middleware may already guarantee that the field is a string. If so, repeating a type check inside the handler may be unnecessary. If no such boundary exists, the handler needs a defined response for malformed values.
Check how the authenticated user reaches the update query. A body field such as profileId is untrusted input. The patch should not replace a session-derived identifier with that field while simplifying the code. An authorization regression can hide in an otherwise reasonable refactor.
Then inspect persistence. Validation failure should leave the stored profile unchanged. A handler that writes first and validates afterward can return the expected error while still changing data. Response assertions alone would miss that defect.
Compare tests with independent requirements
The Claude Code best-practices guide emphasizes giving the tool ways to verify its work. As a reviewer, check that those verification steps are meaningful. A generated test can mirror the implementation closely enough to preserve the same wrong assumption.
For this task, include a whitespace-only string, a missing field, a numeric field, and a valid trimmed name. Assert the stored record after each failed request. Add a request attempting to target another profile and confirm the access boundary under the application’s public error policy.
Use fixtures that make unintended changes visible. If every test starts with an empty display name, a failed update that clears the name may go unnoticed. Start with a nonempty value and verify that it remains unchanged after rejection.
Exercise storage effects in a small review model
For readers comfortable with JavaScript, this executable model makes those questions concrete. The principal is already trusted test input; no login, token verification or HTTP middleware is implemented. Rows are a trusted in-memory array with unique tenant/user pairs. The body is parsed JSON, so custom getters, proxies and class instances are outside this input contract. The function models an owner-only edit policy and returns status/code objects that a real route would have to map deliberately.
function updateProfile(rows, principal, body) {
if (!principal || typeof principal.tenantId !== "string" ||
principal.tenantId === "" || typeof principal.userId !== "string" ||
principal.userId === "") {
return { status: 401, code: "authentication_required" };
}
if (body === null || typeof body !== "object" || Array.isArray(body) ||
Object.keys(body).length !== 1 ||
!Object.hasOwn(body, "displayName") ||
typeof body.displayName !== "string") {
return { status: 422, code: "invalid_profile_input" };
}
const displayName = body.displayName.trim();
const length = Array.from(displayName).length;
if (length < 1 || length > 80) {
return { status: 422, code: "invalid_display_name" };
}
const row = rows.find(item =>
item.tenantId === principal.tenantId && item.userId === principal.userId
);
if (!row) return { status: 404, code: "profile_not_found" };
row.displayName = displayName;
return { status: 200, displayName };
}
Put this next assertion block after the function in a Node ES module. The other tenant deliberately appears first, with the same user identifier. That makes removing the tenant predicate observable instead of allowing array order to hide the error. Each failed request starts with nonempty names and must preserve the entire array.
import assert from "node:assert/strict";
const initial = [
{ tenantId: "t2", userId: "u1", displayName: "Other tenant" },
{ tenantId: "t1", userId: "u1", displayName: "Mai" },
{ tenantId: "t1", userId: "u2", displayName: "Binh" }
];
const principal = { tenantId: "t1", userId: "u1" };
for (const body of [null, [], {}, {displayName: 4}, {displayName: " "},
{displayName: "x".repeat(81)},
{displayName: "Ada", userId: "u2"}]) {
const rows = structuredClone(initial);
assert.equal(updateProfile(rows, principal, body).status, 422);
assert.deepEqual(rows, initial);
}
const rows = structuredClone(initial);
assert.deepEqual(updateProfile(rows, principal, {displayName: " Nguyễn An "}),
{status: 200, displayName: "Nguyễn An"});
assert.deepEqual(rows, initial.map((row, index) =>
index === 1 ? {...row, displayName: "Nguyễn An"} : row));
The exact block passes on Node 22.23.2. In the review fixture, deliberately deleting the tenant condition makes the final stored-state assertion fail: the other tenant’s first row changes. Replacing the initial validation snippet with this reference is a manually authored teaching comparison; no Claude session or production API was exercised. A passing in-memory model does not prove a database query has the same predicate, that a transaction is atomic or that an authenticated principal cannot be forged at the real boundary.
OWASP API1:2023 calls for object-level authorization on the action and record being accessed. A matching user identifier alone is not a general authorization mechanism. This example implements only its stated owner-and-tenant policy; delegated access, shared profiles and administrator actions require their own policy. At the persistence boundary, carry the actual authorization conditions into the write rather than trusting this model as a security certificate.
Additional boundary checks should follow the declared representation. The fixture accepts eighty emoji code points and rejects eighty-one; combining sequences can occupy multiple code points even when a person sees one character. It does not enforce a grapheme limit, normalize Unicode or validate all possible personal names. Invalid principal and missing-row cases return their stated codes without modifying any record. These distinctions keep a validation repair from quietly becoming an unrelated name-policy change.
Read the complete diff for scope changes
A repair may touch shared schemas, generated client types, and error mappers. Those changes can be necessary, but each should connect to the requested behavior. Inspect any new dependency or changed configuration separately; it may affect the runtime far beyond the profile endpoint.
Avoid accepting broad formatting changes merely because the functional lines are correct. They increase review effort and can hide accidental edits. If the repository has an established formatter, a focused formatting adjustment may be appropriate, but the final patch should remain explainable as one task.
Preserve preexisting edits and identify the base commit. If another developer changed the same schema during the agent’s run, the combined result needs a new review of the shared contract. A successful test from an earlier state does not automatically validate a later merge.
Check the user-visible failure path
A stable error code lets the client choose a field message without parsing prose. Confirm that the status and error shape match existing endpoint conventions. A new raw exception response can break the client even when the validator correctly identifies invalid input.
Consider normalization carefully. Trimming surrounding whitespace is the stated behavior here. Changing case, removing internal spaces, or restricting Unicode would add product rules that the task did not request. International names are a reason to keep validation tied to explicit requirements rather than a narrow local assumption about what a name looks like.
Run the relevant checks after the final diff is settled and record their actual completion. If an integration test needs unavailable infrastructure, state that limitation and use the strongest local evidence available without claiming full integration coverage.
The acceptance decision should follow the request path: invalid data receives the documented response, unauthorized targets remain inaccessible, valid data is stored correctly, and failed requests preserve the old record. A generated patch is ready when those claims have evidence and the remaining diff has a clear purpose.