Skip to content

feat(api): add session route shell#81

Closed
Vaibhaav-Tiwari wants to merge 8 commits into
feat/projects-openapi-codegenfrom
session-route-shell-pr59
Closed

feat(api): add session route shell#81
Vaibhaav-Tiwari wants to merge 8 commits into
feat/projects-openapi-codegenfrom
session-route-shell-pr59

Conversation

@Vaibhaav-Tiwari
Copy link
Copy Markdown
Collaborator

Summary

  • add the first session route-shell slice on top of the code-first OpenAPI branch
  • mount 501-backed session routes for list, spawn, get, restore, and send
  • add Go wire DTOs and specgen operations, then regenerate openapi.yaml
  • keep unclear/deferred routes unregistered for now: kill, patch/rename, patches, remap, orchestrators, platform read-models
  • add LF normalization for the generated OpenAPI YAML drift check

Testing

  • cd backend && go generate ./...
  • cd backend && go test ./internal/httpd/apispec/...
  • cd backend && go test ./internal/httpd/controllers/...
  • cd backend && go test ./...

Notes

This PR is stacked on #59 because OpenAPI is generated from Go contract code there. It intentionally does not hand-edit OpenAPI YAML beyond the generated artifact.

i-trytoohard and others added 6 commits June 1, 2026 21:53
PR was building, leaving #24 conflicting on nearly every file. The route shell
itself is now redundant, but two pieces of #24 are genuinely net-new and absent
from main — salvage them here, rebuilt on top of #47's merged code:

- Code-first OpenAPI: apispec/specgen reflects the controllers' request/response
  types and project DTOs (the same types the handlers use at runtime) into
  openapi.yaml via swaggest. `cmd/genspec` + `go:generate` regenerate the
  committed, embedded spec; a drift test (TestBuild_MatchesEmbedded) and a route
  parity test (TestRouteSpecParity) fail CI if the spec and the code disagree.
  This replaces main's hand-maintained openapi.yaml so the "single source of
  truth" claim is actually enforced, not aspirational.

- Typed frontend client: frontend/src/api/schema.d.ts is generated from that
  spec via openapi-typescript (`npm run gen:api`), consumed by a small
  openapi-fetch client. The frontend now gets its types from the daemon
  contract instead of hand-maintaining them.

specgen lives outside apispec (which controllers import for the 501 stub) to
avoid an import cycle. Handlers now encode named response DTOs
(controllers/dto.go) instead of map[string]any so the generator reflects the
real wire shapes. A gen-verify CI job regenerates both artifacts and fails on a
stale commit.

Tradeoff: the generated spec drops the hand-authored examples / x-rest-audit
notes from #47's openapi.yaml; those can be re-added as operation metadata in
specgen if wanted. Behaviour-only patch (no handler logic changes).

Supersedes the codegen + frontend parts of #24. Refs #20, #47.
…chema names

Resolve the four review comments on #59:

- ProjectOrDegraded.MarshalJSON now errors when neither Project nor Degraded
  is set instead of silently emitting `{"project": null}`, which would breach
  the required oneOf[Project, Degraded] contract.
- requestBody.required: true is now set for addProject and updateProjectConfig
  via WithCustomize — swaggest left it absent (== optional) before.
- schemaName replaces the TrimPrefix catch-all with an exhaustive default→clean
  map; an unrecognised type is returned verbatim so it surfaces in the diff
  rather than silently colliding with an existing schema.
- nonNullableSlices strips the spurious "null" swaggest unions into every Go
  slice, so `projects` is `Summary[]` not `Summary[] | null`; the list handler
  normalises a nil slice to [] so the wire matches the non-nullable schema.

Regenerated openapi.yaml + frontend schema.d.ts. Refs #59.
Addresses the P1 follow-up on #59: returning an error from
ProjectOrDegraded.MarshalJSON does not "surface" the contract violation —
envelope.WriteJSON discards the encode error after the 200 status and a partial
JSON frame have already been flushed, leaving the client with a truncated,
unparseable 200 (worse than the previous null).

Validate the invariant upstream instead: newGetProjectResponse now returns an
error when the GetResult sets neither Project nor Degraded, and the get handler
maps that to a 500 envelope before any status/body is written. MarshalJSON keeps
the error branch only as an unreachable last-resort backstop, with the comment
corrected to say so. Adds TestProjectsAPI_GetEmptyResultIs500 to lock the
clean-500 behavior.

Refs #59.
…pec API, regen)

Rebased onto main, which landed several changes that intersect this branch:

- writeProjectError is now 3-arg (status derived from project.Error.Kind); the
  get handler's degenerate-GetResult guard now calls the 3-arg form.
- apispec.Spec dropped its YAML() method; add apispec.Embedded() returning the
  raw embedded bytes, used by the generator's drift + route-parity tests.
- #62 removed Runtime/Reactions/ReactionConfig from the project types, so the
  regenerated openapi.yaml (and frontend schema.d.ts) drop those, and the dead
  ReactionConfig entry is removed from the schema-name map.
- CI go.yml: keep main's new golangci-lint `lint` job alongside the gen-verify
  job, both reading the Go version from go.mod.

go build/vet/test all pass; spec + TS regenerate with no drift; gofmt clean.
Three findings from the lint CI job (golangci-lint v2.12.2, which can't run
locally against the go1.25 module):

- gosec G306: genspec writes openapi.yaml with 0o600 instead of 0o644.
- revive: add the missing doc comment on ProjectOrDegraded.MarshalJSON.
- staticcheck SA1019: r.InterceptDefName is deprecated; pass
  jsonschema.InterceptDefName(schemaName) via DefaultOptions instead.

No behavior change — the regenerated spec is byte-identical (the InterceptDefName
move produces the same output). build/vet/test pass; gofmt clean.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 2, 2026

Greptile Summary

This PR adds the first session route-shell slice on top of the OpenAPI code-generation branch, mounting 501-backed handlers for list, spawn, get, restore, and send, and regenerating openapi.yaml from the new Go wire DTOs and sessionOperations().

  • Adds SessionsController with five stub handlers, wires *session.Manager into APIDeps and NewAPI, and plugs the controller into the /api/v1 router group.
  • Adds Go wire DTOs (SessionIDParam, ListSessionsQuery, SpawnSessionRequest, ListSessionsResponse, SessionResponse, SendSessionRequest, SendSessionResponse) and a sessionOperations() function in specgen/build.go.
  • Adds a .gitattributes rule enforcing LF line endings on the generated YAML to prevent drift false-positives on Windows.

Confidence Score: 5/5

All five session handlers are 501 stubs that never touch real data; no existing behavior is changed.

Every route returns NOT_IMPLEMENTED today, so there is no runtime path through the new session code that can fail, corrupt state, or expose data. The spec and DTO additions are straightforward and covered by the existing drift test. The only gap — missing enum constraints on domain string fields — is a spec quality concern for future client generators, not a correctness issue in the running service.

No files require special attention; the generated openapi.yaml is produced deterministically by the drift-checked build pipeline.

Important Files Changed

Filename Overview
backend/internal/httpd/controllers/sessions.go New file: registers five 501-stub handlers for the session surface; all handlers delegate to apispec.NotImplemented and never dereference Mgr.
backend/internal/httpd/controllers/dto.go Adds session wire DTOs; ListSessionsQuery.Project correctly typed as domain.ProjectID; enum-valued domain fields (kind, status, harness) surfaced as unconstrained strings in spec.
backend/internal/httpd/apispec/specgen/build.go Adds sessionOperations(), moves hardcoded tag into the operation struct, adds schemaNames entries for new session types.
backend/internal/httpd/api.go Wires *session.Manager into APIDeps and SessionsController and plugs sessions into the router group.
backend/internal/httpd/controllers/sessions_test.go Verifies all five routes return 501 NOT_IMPLEMENTED and three unregistered routes return 404 ROUTE_NOT_FOUND.
backend/internal/httpd/apispec/openapi.yaml Generated artifact: adds session paths and component schemas; kind/status/harness/activity.state lack enum constraints.
.gitattributes Enforces LF line endings on openapi.yaml to prevent spurious YAML drift on Windows checkouts.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Router as chi Router (/api/v1)
    participant SC as SessionsController
    participant Spec as apispec.NotImplemented

    Client->>Router: GET /api/v1/sessions
    Router->>SC: list(w, r)
    SC->>Spec: NotImplemented
    Spec-->>Client: 501 NOT_IMPLEMENTED

    Client->>Router: POST /api/v1/sessions
    Router->>SC: spawn(w, r)
    SC->>Spec: NotImplemented
    Spec-->>Client: 501 NOT_IMPLEMENTED

    Client->>Router: "GET /api/v1/sessions/{id}"
    Router->>SC: get(w, r)
    SC->>Spec: NotImplemented
    Spec-->>Client: 501 NOT_IMPLEMENTED

    Client->>Router: "POST /api/v1/sessions/{id}/restore"
    Router->>SC: restore(w, r)
    SC->>Spec: NotImplemented
    Spec-->>Client: 501 NOT_IMPLEMENTED

    Client->>Router: "POST /api/v1/sessions/{id}/send"
    Router->>SC: send(w, r)
    SC->>Spec: NotImplemented
    Spec-->>Client: 501 NOT_IMPLEMENTED
Loading

Reviews (3): Last reviewed commit: "fix(api): remove unused session schema n..." | Re-trigger Greptile

Comment thread backend/internal/httpd/controllers/dto.go Outdated
Comment thread backend/internal/httpd/api.go Outdated
Comment thread backend/internal/httpd/apispec/specgen/build.go
@AgentWrapper AgentWrapper force-pushed the feat/projects-openapi-codegen branch 2 times, most recently from 064c188 to 019c985 Compare June 2, 2026 16:18
@Vaibhaav-Tiwari
Copy link
Copy Markdown
Collaborator Author

Closing this as superseded by #68, which merged the newer service-layer session API work on main. Any remaining useful pieces from this branch, like generated OpenAPI LF normalization or dropped-route test coverage, should be carried forward as a fresh small PR from main if still needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants