OPS-T1 route-shape inventory
OPS-T1 route-shape inventory
Section titled “OPS-T1 route-shape inventory”Status: DRAFT-S11 — awaiting Liam ratification at S11 close. Source:
find app/api -name 'route.ts'on commit20404054(ast-dataflow-tooling), plus empirical grep/AST sweep run in kh-ast-S11 R-WP-S11-A. Purpose: Ground thewrap-define-routecodemod design in the real distribution of route handler shapes across the KH API surface.
1. Total count
Section titled “1. Total count”find app/api -name 'route.ts' returns 193 files — matching the Wave 0-A
figure (§2.1, S10-wave-0-synthesis.md).
2. Shape taxonomy
Section titled “2. Shape taxonomy”Each route file is assigned a primary shape based on the most
significant structural feature. Shapes are mutually exclusive by
assignment (a file is placed in the first matching bucket in priority
order: CRON > MCP > NAKED_NO_AUTH > multi-method variants > single-method variants).
Shape definitions
Section titled “Shape definitions”| Shape | Description | Codemod-relevant? |
|---|---|---|
AUTH_PLAIN | Single-method, auth-wrapped, no dynamic params, no request body | Yes — simplest case |
PARAM_BODY | Single-method, auth-wrapped, dynamic path segment [id], plus request.json() / parseBody() | Yes — needs params context |
BODY_VALIDATED | Single-method, auth-wrapped, no path params, reads request.json() + parseBody() | Yes — straightforward body |
PARAM | Single-method, auth-wrapped, dynamic path segment only (no body) | Yes — GET-only with params |
MULTI_PARAM_BODY | Two or more HTTP methods, dynamic path segment, plus body | Partial — multi-method needs per-method wrapping |
MULTI_BODY | Two or more HTTP methods, no path params, plus body | Partial — multi-method |
CRON | Route under app/api/cron/ — uses cron-secret auth, not getAuthorisedClient | MANUAL — different auth model |
NAKED_NO_AUTH | No getAuthorisedClient / getAuthenticatedClient call (public or system routes) | MANUAL — no auth wrapper to preserve |
MULTI_PARAM | Two or more HTTP methods, dynamic path segment, no body | Partial — multi-method |
MCP | Route under app/api/mcp/ — uses MCP transport abstraction | MANUAL — bespoke transport handler |
3. Distribution
Section titled “3. Distribution”| Shape | Count | % of total | Mechanisability |
|---|---|---|---|
AUTH_PLAIN | 40 | 20.7 % | MECHANISABLE |
PARAM_BODY | 40 | 20.7 % | MECHANISABLE |
BODY_VALIDATED | 31 | 16.1 % | MECHANISABLE |
PARAM | 26 | 13.5 % | MECHANISABLE |
MULTI_PARAM_BODY | 19 | 9.8 % | NEEDS-REVIEW |
MULTI_BODY | 17 | 8.8 % | NEEDS-REVIEW |
CRON | 9 | 4.7 % | MANUAL |
NAKED_NO_AUTH | 6 | 3.1 % | MANUAL |
MULTI_PARAM | 4 | 2.1 % | NEEDS-REVIEW |
MCP | 1 | 0.5 % | MANUAL |
| Total | 193 | 100 % |
Mechanisability summary:
| Verdict | Count | % |
|---|---|---|
| MECHANISABLE (codemod handles end-to-end) | 137 | 71.0 % |
| NEEDS-REVIEW (codemod wraps; human confirms per-method schema) | 40 | 20.7 % |
| MANUAL (codemod skips; generates report entry) | 16 | 8.3 % |
Note on Wave 0-C estimate (10–20 % mechanisation): The Wave 0-C feasibility report quoted 10–20 % because it assessed mechanisation of the entire OPS-T1 migration cost — including the ResponseSchema authoring step that remains manual for all 156 non-fetcher-only routes. The 71 % figure above applies only to the wrapper-insertion sub-task (structural rewriting of the handler signature); the schema-authoring work for routes outside the R-WP17 37-interface baseline is unchanged and remains manual. See §5 for the reconciliation.
4. Shape examples
Section titled “4. Shape examples”4.1 AUTH_PLAIN — 40 routes
Section titled “4.1 AUTH_PLAIN — 40 routes”Single exported method, getAuthorisedClient / getAuthenticatedClient, no
dynamic path segment, no request body. Typically read-only GET handlers.
api/insights/route.tsapi/activity/route.tsapi/dashboard/route.tsapi/intelligence/workspaces/[id]/metrics/route.tsapi/review/stats/route.tsCanonical pattern:
export async function GET(request: NextRequest) { const auth = await getAuthorisedClient(['admin', 'editor']); if (!auth.success) return authFailureResponse(auth); const { supabase } = auth; // ... query + return NextResponse.json(payload)}Some AUTH_PLAIN routes use withRequestContext wrapping (see §4.11):
export const GET = withRequestContext(async (request: NextRequest) => { const auth = await getAuthorisedClient(['admin', 'editor']); // ...});4.2 PARAM_BODY — 40 routes
Section titled “4.2 PARAM_BODY — 40 routes”Single exported method, auth-wrapped, dynamic path segment [id] (or
[canonical_name], [slug], etc.), plus request.json() + parseBody().
Typically POST/PATCH/PUT handlers on resource sub-endpoints.
api/entities/[canonical_name]/type/route.tsapi/entities/[canonical_name]/metadata/route.tsapi/items/[id]/classify/route.tsapi/items/[id]/summarise/route.tsapi/bids/[id]/questions/[qId]/route.tsNext.js 15 Promise<params> style is used in approximately 78 of the 92
parameterised routes (all shapes combined); the older synchronous style
persists in a small number of files that have not been migrated.
4.3 BODY_VALIDATED — 31 routes
Section titled “4.3 BODY_VALIDATED — 31 routes”Single exported method, auth-wrapped, no path params, reads request body. Typically POST endpoints: create, search, embed, export.
api/embed/route.tsapi/search/route.tsapi/extract/route.tsapi/analysis/route.tsapi/digest/generate/route.ts4.4 PARAM — 26 routes
Section titled “4.4 PARAM — 26 routes”Single exported method, auth-wrapped, dynamic path segment, no request body. Typically GET handlers for individual resource reads.
api/entities/[canonical_name]/route.tsapi/items/[id]/layers/route.tsapi/items/[id]/history/route.tsapi/bids/[id]/coverage/route.tsapi/review/assignments/[id]/route.ts4.5 MULTI_PARAM_BODY — 19 routes
Section titled “4.5 MULTI_PARAM_BODY — 19 routes”Two or more exported HTTP methods in the same file, with dynamic path segment and request body. The most complex shape for the codemod.
api/items/[id]/route.ts (GET + PUT + DELETE)api/items/[id]/workspaces/route.ts (GET + POST)api/guides/[slug]/route.ts (GET + PUT + DELETE)api/intelligence/workspaces/[id]/route.ts (GET + PATCH)api/intelligence/workspaces/[id]/sources/[sourceId]/route.ts (GET + PUT + DELETE)4.6 MULTI_BODY — 17 routes
Section titled “4.6 MULTI_BODY — 17 routes”Two or more exported HTTP methods, no dynamic path segment, request body present on at least one method.
api/layers/route.ts (GET + POST)api/quality/route.ts (GET + POST)api/bids/route.ts (GET + POST)api/governance/route.ts (GET + POST)api/tags/route.ts (GET + POST)4.7 CRON — 9 routes
Section titled “4.7 CRON — 9 routes”Located under app/api/cron/. Use cron-secret validation (checking
x-vercel-cron header or CRON_SECRET environment variable) rather than
getAuthorisedClient. Use createServiceClient() to bypass user-scoped RLS.
api/cron/process-queue/route.tsapi/cron/intelligence-poll/route.tsapi/cron/review-cadence/route.tsapi/cron/classification-quality/route.tsapi/cron/coverage-alerts/route.tsapi/cron/content-gaps/route.tsapi/cron/freshness-transitions/route.tsapi/cron/intelligence-cleanup/route.tsapi/cron/quality-score/route.ts4.8 NAKED_NO_AUTH — 6 routes
Section titled “4.8 NAKED_NO_AUTH — 6 routes”No getAuthorisedClient or getAuthenticatedClient call. Includes public
system routes and special-purpose handlers.
api/health/route.ts — system health check (in PUBLIC_ROUTES allowlist)api/feeds/[workspaceId]/rss/route.ts — public RSS feedapi/feeds/[workspaceId]/rss/filtered/route.ts — public RSS feed (filtered)api/oauth/decision/route.ts — OAuth consent flowapi/admin/taxonomy-sync/callback/route.ts — admin webhook callbackapi/plugin/download/route.ts — plugin download (API key auth)4.9 MULTI_PARAM — 4 routes
Section titled “4.9 MULTI_PARAM — 4 routes”Two or more HTTP methods, dynamic path segment, no request body.
api/items/[id]/images/route.ts (GET + POST)api/items/[id]/files/route.ts (GET + DELETE)api/bids/[id]/templates/route.ts (GET + POST)api/intelligence/profiles/[id]/route.ts (GET + PUT + DELETE — no body on GET)4.10 MCP — 1 route
Section titled “4.10 MCP — 1 route”api/mcp/[transport]/route.ts (GET + POST + DELETE)Uses WebStandardStreamableHTTPServerTransport directly (per CLAUDE.md
gotcha). Three methods handle different transport lifecycle events. Not
wrappable with defineRoute() — the shape is protocol-handler, not
data-API.
4.11 withRequestContext wrapping (cross-cutting sub-variant)
Section titled “4.11 withRequestContext wrapping (cross-cutting sub-variant)”7 route files use withRequestContext from @/lib/logger, which changes the
export syntax from export async function METHOD() to
export const METHOD = withRequestContext(async () => { … }). This sub-variant
crosses AUTH_PLAIN, PARAM_BODY, and MULTI_BODY primary shapes. The
codemod must detect this pattern and emit a NEEDS-REVIEW entry (the
withRequestContext wrapper must be preserved as an outer wrapper around
defineRoute).
Representative examples:
api/items/route.ts (BODY_VALIDATED + withRequestContext → POST)api/items/[id]/classify/route.ts (PARAM_BODY + withRequestContext → POST)api/items/[id]/summarise/route.ts (PARAM_BODY + withRequestContext → POST)5. Reconciliation with Wave 0-C estimate
Section titled “5. Reconciliation with Wave 0-C estimate”Wave 0-C (S10-programmatic-migration-feasibility.md §2 W1) estimated
~15–25 % mechanisation for OPS-T1 overall. The 71 % figure in §3 above
measures a narrower axis — wrapper-insertion only — and is compatible with
the Wave 0-C figure once the schema-authoring component is reintroduced:
| Component | Mechanisability | Route count |
|---|---|---|
| Wrapper insertion (137 MECHANISABLE routes) | ~100 % codemod handles | 137 |
| Wrapper insertion (40 NEEDS-REVIEW routes) | ~50 % (human confirms schema per method) | 40 |
| Wrapper insertion (16 MANUAL routes) | 0 % (codemod skips) | 16 |
| ResponseSchema authoring (37 R-WP17 fetcher-only) | Provided by type-drift-baseline.json | 37 |
| ResponseSchema authoring (156 other routes) | Manual (no known schema) | 156 |
The Wave 0-C 20 % headline is a weighted average across all components. The
codemod can automate roughly 137 × wrapper + 37 × schema injection = 174
semi-complete transformations, but 156 routes still need a human to author
the ResponseSchema Zod object before defineRoute() can be called.
No escalation required — the inventory finding is consistent with Wave 0-C, not a contradiction. The 71 % wrapper-mechanisation figure should be surfaced in the PRODUCT.md and TECH.md to set accurate expectations.
6. Codemod mechanisability verdict per shape
Section titled “6. Codemod mechanisability verdict per shape”| Shape | Verdict | Rationale |
|---|---|---|
AUTH_PLAIN | MECHANISABLE | Single method, known auth pattern, no params. Codemod inserts wrapper, injects z.unknown() placeholder schema or known schema from baseline. |
PARAM_BODY | MECHANISABLE | Single method, known auth pattern. Codemod extracts params destructure, preserves body parsing. |
BODY_VALIDATED | MECHANISABLE | Single method, no params. Body schema already present in parseBody() call — can be lifted as ResponseSchema hint. |
PARAM | MECHANISABLE | Single method, GET-only. No body. Schema is always the response type — must come from baseline or remain placeholder. |
MULTI_PARAM_BODY | NEEDS-REVIEW | Multiple methods. Codemod wraps each method individually; human confirms per-method ResponseSchema. |
MULTI_BODY | NEEDS-REVIEW | Multiple methods. Same as above. |
MULTI_PARAM | NEEDS-REVIEW | Multiple methods, no body. |
CRON | MANUAL | Different auth model (createServiceClient, cron-secret). No user context. defineRoute() contract does not apply. |
NAKED_NO_AUTH | MANUAL | No auth wrapper — defineRoute() presupposes an authenticated handler. Public routes stay as-is. |
MCP | MANUAL | Protocol handler, not a data API. |
withRequestContext sub-variant | NEEDS-REVIEW | Must preserve outer withRequestContext wrapper. Shape is complex enough to require human confirmation. |
7. Source notes
Section titled “7. Source notes”- Route list generated:
find app/api -name 'route.ts'onast-dataflow-toolingat20404054. - Shape classification: Python grep-based script run against the worktree (S11 kh-ast-S11 R-WP-S11-A).
withRequestContextcount (7 files) verified bygrep -rl "withRequestContext"inapp/api/.- Auth client count (177 files) verified by
grep -rl "getAuthorisedClient\|getAuthenticatedClient"inapp/api/. - Next.js 15
Promise<params>style count (78 files) verified bygrep -rl "params: Promise<{"inapp/api/.