Both Discover routes publish their 200 body as {"type": "object", "additionalProperties": true}. The actual field list survives only as English prose inside the description, so the generated OpenAPI document describes the endpoints but does not specify them — no field names, no types, no optionality, nothing a client generator can consume.
This is worth fixing because the module's own docstring argues for the two-path design on the grounds that a published contract matters.
Evidence
packages/server/src/routes/discover.ts:110 (listing) and :143 (expansion) both declare:
response: {
200: {
description: "...",
type: "object",
additionalProperties: true,
The header comment, routes/discover.ts:9-18, explains why there are two paths rather than one ?dir= route:
Paddock publishes an OpenAPI 3 document GENERATED from these Fastify route schemas … and one path cannot describe two different 200 bodies there — it would have to be declared additionalProperties: true with no shape at all, and the published contract would silently stop describing the endpoint.
The split was made to avoid exactly the state both routes shipped in.
Confirmed published: openapi-site/open-api.json:510 (/api/discover) and :553 (/api/discover/sessions).
The shapes that are missing
Already fully typed in TypeScript — this is a transcription, not a design task:
DiscoverResult — packages/server/src/discover.ts:178-192: { claudeHome, homeDir, scanned, candidates, excluded }
DiscoverCandidate — discover.ts:131-175: path, recordedPath?, name, suggestedSlug, hasGit, gitRemote?, insideHome, sessionCount, filteredCount, lastSessionAt?
excluded — a Partial<Record<DiscoverExclusion, number>> over a closed vocabulary (discover.ts:87-97): no-recorded-cwd, missing, system-path, temp-root, paddock-internal, home-root, outside-home, already-managed, no-git, no-sessions
DiscoverSessions — discover.ts:195-200: { path, sessions, filtered }, where sessions[] is AdoptableCandidate (packages/server/src/adoptable.ts:88-97) and filtered[].reason is the same closed vocabulary as GET …/adoptable-chats (adoptable.ts:141)
Failure scenario
A client generates a typed SDK from open-api.json. Both Discover operations come back returning Record<string, unknown>. candidates[].suggestedSlug — the value you must pass to POST /api/projects to do anything with the result — is invisible to the generator, so the one workflow the routes exist to support is untyped end to end.
Severity
Low. No runtime defect; the routes work and the web client (which is not generated) is unaffected.
Blocks a docs claim
Yes, in the sense that it forces a choice. reference/api.md has zero occurrences of "discover" and needs these routes documented. Either the shapes get hand-written into the reference page — which then drifts from source at the next change — or they go into the route schemas and the page can point at the generated spec, as it does for other routes. The second is clearly better; this issue exists so that decision is made explicitly rather than by default.
Note on the generated file
Do not hand-edit openapi-site/open-api.json. It is generated by scripts/dump-openapi.mjs, which reads dist/ — regenerating on a stale build reverts other branches' route schemas. Fix routes/discover.ts and let the release workflow regenerate.
Related: the same description contains a unit error about excluded (filed separately).
Both Discover routes publish their 200 body as
{"type": "object", "additionalProperties": true}. The actual field list survives only as English prose inside thedescription, so the generated OpenAPI document describes the endpoints but does not specify them — no field names, no types, no optionality, nothing a client generator can consume.This is worth fixing because the module's own docstring argues for the two-path design on the grounds that a published contract matters.
Evidence
packages/server/src/routes/discover.ts:110(listing) and:143(expansion) both declare:The header comment,
routes/discover.ts:9-18, explains why there are two paths rather than one?dir=route:The split was made to avoid exactly the state both routes shipped in.
Confirmed published:
openapi-site/open-api.json:510(/api/discover) and:553(/api/discover/sessions).The shapes that are missing
Already fully typed in TypeScript — this is a transcription, not a design task:
DiscoverResult—packages/server/src/discover.ts:178-192:{ claudeHome, homeDir, scanned, candidates, excluded }DiscoverCandidate—discover.ts:131-175:path,recordedPath?,name,suggestedSlug,hasGit,gitRemote?,insideHome,sessionCount,filteredCount,lastSessionAt?excluded— aPartial<Record<DiscoverExclusion, number>>over a closed vocabulary (discover.ts:87-97):no-recorded-cwd,missing,system-path,temp-root,paddock-internal,home-root,outside-home,already-managed,no-git,no-sessionsDiscoverSessions—discover.ts:195-200:{ path, sessions, filtered }, wheresessions[]isAdoptableCandidate(packages/server/src/adoptable.ts:88-97) andfiltered[].reasonis the same closed vocabulary asGET …/adoptable-chats(adoptable.ts:141)Failure scenario
A client generates a typed SDK from
open-api.json. Both Discover operations come back returningRecord<string, unknown>.candidates[].suggestedSlug— the value you must pass toPOST /api/projectsto do anything with the result — is invisible to the generator, so the one workflow the routes exist to support is untyped end to end.Severity
Low. No runtime defect; the routes work and the web client (which is not generated) is unaffected.
Blocks a docs claim
Yes, in the sense that it forces a choice.
reference/api.mdhas zero occurrences of "discover" and needs these routes documented. Either the shapes get hand-written into the reference page — which then drifts from source at the next change — or they go into the route schemas and the page can point at the generated spec, as it does for other routes. The second is clearly better; this issue exists so that decision is made explicitly rather than by default.Note on the generated file
Do not hand-edit
openapi-site/open-api.json. It is generated byscripts/dump-openapi.mjs, which readsdist/— regenerating on a stale build reverts other branches' route schemas. Fixroutes/discover.tsand let the release workflow regenerate.Related: the same description contains a unit error about
excluded(filed separately).