feat(steps): a reply that sets no status is a 200; inline Express handlers keep their replies

- effects.ts implicitResponseStatus: a body-sending reply with no status in its chain (res.json / send / render, reply.send, c.json, NextResponse.json, JSONResponse / jsonify / render_template, Rails render, Laravel response()->json) is a 200; a variable status, end, sendStatus and redirects stay as they were
- branch-guards callSiteInTree: a status set by the statement just before the reply (`res.status(202); res.json(user)`) is that reply's — looked back within the block, only a statement that IS the status call counts
- steps.ts: explicit chain/args → set-before → implicit 200
- express.ts: an inline handler's reply calls (`res.status(404).json(…)`, `res.json(user)`) are references at their own line and column instead of framework noise, so the route's own reply box exists
- tests: servers fixture (inline route's 200 beside the service's 404; a 202 set before), ui-effects
- CHANGELOG

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REFyW9hmNrxhwN5wxRoAkC
This commit is contained in:
Colby McHenry
2026-08-29 12:00:04 -05:00
co-authored by Claude Fable 5
parent 77dc0ad2eb
commit 02430ccc32
7 changed files with 144 additions and 9 deletions
+5 -2
View File
@@ -42,7 +42,7 @@ import type { Edge, Language, Node, UnresolvedReference } from '../../types';
import { badRequest, intParam, notFound } from './respond';
import { createSiteReader } from './when';
import type { SiteTrigger } from '../../graph/branch-guards';
import { classifyEffect, responseStatus, type Effect } from './effects';
import { classifyEffect, implicitResponseStatus, responseStatus, type Effect } from './effects';
import { looksLikeComponent, routeRoots } from './route-roots';
import { nextRouteForFile } from '../../resolution/frameworks/nextjs';
import { splitRouteName } from './routes';
@@ -639,7 +639,10 @@ export async function buildSteps(cg: CodeGraph, projectRoot: string, query: URLS
if (effect.category === 'response') {
// `NextResponse.json(user, { status: 201 })`: the code sits in an object
// the abbreviation reduced to its keys; the site reader kept it.
const status = responseStatus(text, args, ref.referenceKind) ?? (usable && typeof site.status === 'number' ? site.status : null);
// — and a body-sending reply that sets none is a 200, so a success row
// says so beside the 401s.
const status =
responseStatus(text, args, ref.referenceKind) ?? (usable && typeof site.status === 'number' ? site.status : null) ?? implicitResponseStatus(text);
if (status !== null) wireSite.status = status;
}
link(step, target, 'effect', fold.chain, [...fold.whens, when], wireSite, null, trigger ?? (await triggerAt(fold.node, at)));