Skip to content

The Discover routes publish opaque additionalProperties: true bodies, so a generated client sees no fields on either endpoint #822

Description

@edspencer

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:

  • DiscoverResultpackages/server/src/discover.ts:178-192: { claudeHome, homeDir, scanned, candidates, excluded }
  • DiscoverCandidatediscover.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
  • DiscoverSessionsdiscover.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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    XSExtra small: under ~50 linesbugSomething isn't workingdocumentationImprovements or additions to documentation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions