Fix golangci-lint unused failure — export OpenAPI response types
CI / Canvas Deploy Reminder (pull_request) Blocked by required conditions
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 7s
CI / Python Lint & Test (pull_request) Successful in 4s
E2E API Smoke Test / detect-changes (pull_request) Successful in 7s
E2E Chat / detect-changes (pull_request) Successful in 9s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 6s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 5s
Harness Replays / detect-changes (pull_request) Successful in 2s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 4s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 3s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
gate-check-v3 / gate-check (pull_request) Successful in 9s
qa-review / approved (pull_request) Failing after 3s
security-review / approved (pull_request) Failing after 3s
sop-checklist / review-refire (pull_request) Has been skipped
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Successful in 4s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m8s
CI / Canvas (Next.js) (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
E2E Chat / E2E Chat (pull_request) Successful in 5s
Harness Replays / Harness Replays (pull_request) Successful in 3s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m7s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2m19s
CI / Platform (Go) (pull_request) Successful in 5m50s
CI / all-required (pull_request) Successful in 14m14s
audit-force-merge / audit (pull_request) Successful in 3s

CI / Platform (Go) flagged `internal/handlers/schedules.go:19:6: type
errorResponse is unused (unused)` because golangci-lint's `unused`
checker doesn't recognise swaggo's `@Success {object} errorResponse`
annotations as references — the types are only used in comments
from Go's perspective.

Fix: export all OpenAPI request/response types. Exported names are
skipped by the unused-checker (package consumers may exist outside
the package, even if absent inside it).

Renamed:
- errorResponse → ErrorResponse
- statusResponse → StatusResponse
- createScheduleResponse → CreateScheduleResponse
- runNowResponse → RunNowResponse
- historyEntry → HistoryEntry
- scheduleResponse → ScheduleResponse
- createScheduleRequest → CreateScheduleRequest
- updateScheduleRequest → UpdateScheduleRequest
- scheduleHealthResponse → ScheduleHealthResponse (test ref updated)

Regenerated docs/openapi/swagger.{yaml,json} with new type names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 00:14:16 -07:00
parent f01636cd76
commit f86e151c02
4 changed files with 98 additions and 98 deletions
+27 -27
View File
@@ -42,14 +42,14 @@
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.scheduleResponse"
"$ref": "#/definitions/handlers.ScheduleResponse"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
@@ -85,7 +85,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.createScheduleRequest"
"$ref": "#/definitions/handlers.CreateScheduleRequest"
}
}
],
@@ -93,19 +93,19 @@
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/handlers.createScheduleResponse"
"$ref": "#/definitions/handlers.CreateScheduleResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
@@ -146,19 +146,19 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.statusResponse"
"$ref": "#/definitions/handlers.StatusResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
@@ -201,7 +201,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.updateScheduleRequest"
"$ref": "#/definitions/handlers.UpdateScheduleRequest"
}
}
],
@@ -209,25 +209,25 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.scheduleResponse"
"$ref": "#/definitions/handlers.ScheduleResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
@@ -270,14 +270,14 @@
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.historyEntry"
"$ref": "#/definitions/handlers.HistoryEntry"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
@@ -318,19 +318,19 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.runNowResponse"
"$ref": "#/definitions/handlers.RunNowResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/handlers.errorResponse"
"$ref": "#/definitions/handlers.ErrorResponse"
}
}
}
@@ -338,7 +338,7 @@
}
},
"definitions": {
"handlers.createScheduleRequest": {
"handlers.CreateScheduleRequest": {
"type": "object",
"required": [
"cron_expr",
@@ -362,7 +362,7 @@
}
}
},
"handlers.createScheduleResponse": {
"handlers.CreateScheduleResponse": {
"type": "object",
"properties": {
"id": {
@@ -376,7 +376,7 @@
}
}
},
"handlers.errorResponse": {
"handlers.ErrorResponse": {
"type": "object",
"properties": {
"error": {
@@ -384,7 +384,7 @@
}
}
},
"handlers.historyEntry": {
"handlers.HistoryEntry": {
"type": "object",
"properties": {
"duration_ms": {
@@ -404,7 +404,7 @@
}
}
},
"handlers.runNowResponse": {
"handlers.RunNowResponse": {
"type": "object",
"properties": {
"prompt": {
@@ -418,7 +418,7 @@
}
}
},
"handlers.scheduleResponse": {
"handlers.ScheduleResponse": {
"type": "object",
"properties": {
"created_at": {
@@ -469,7 +469,7 @@
}
}
},
"handlers.statusResponse": {
"handlers.StatusResponse": {
"type": "object",
"properties": {
"status": {
@@ -477,7 +477,7 @@
}
}
},
"handlers.updateScheduleRequest": {
"handlers.UpdateScheduleRequest": {
"type": "object",
"properties": {
"cron_expr": {
+27 -27
View File
@@ -1,6 +1,6 @@
basePath: /
definitions:
handlers.createScheduleRequest:
handlers.CreateScheduleRequest:
properties:
cron_expr:
type: string
@@ -16,7 +16,7 @@ definitions:
- cron_expr
- prompt
type: object
handlers.createScheduleResponse:
handlers.CreateScheduleResponse:
properties:
id:
type: string
@@ -25,12 +25,12 @@ definitions:
status:
type: string
type: object
handlers.errorResponse:
handlers.ErrorResponse:
properties:
error:
type: string
type: object
handlers.historyEntry:
handlers.HistoryEntry:
properties:
duration_ms:
type: integer
@@ -43,7 +43,7 @@ definitions:
timestamp:
type: string
type: object
handlers.runNowResponse:
handlers.RunNowResponse:
properties:
prompt:
type: string
@@ -52,7 +52,7 @@ definitions:
workspace_id:
type: string
type: object
handlers.scheduleResponse:
handlers.ScheduleResponse:
properties:
created_at:
type: string
@@ -87,12 +87,12 @@ definitions:
workspace_id:
type: string
type: object
handlers.statusResponse:
handlers.StatusResponse:
properties:
status:
type: string
type: object
handlers.updateScheduleRequest:
handlers.UpdateScheduleRequest:
properties:
cron_expr:
type: string
@@ -130,12 +130,12 @@ paths:
description: OK
schema:
items:
$ref: '#/definitions/handlers.scheduleResponse'
$ref: '#/definitions/handlers.ScheduleResponse'
type: array
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
security:
- BearerAuth: []
OrgSlugAuth: []
@@ -156,22 +156,22 @@ paths:
name: body
required: true
schema:
$ref: '#/definitions/handlers.createScheduleRequest'
$ref: '#/definitions/handlers.CreateScheduleRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/handlers.createScheduleResponse'
$ref: '#/definitions/handlers.CreateScheduleResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
security:
- BearerAuth: []
OrgSlugAuth: []
@@ -197,15 +197,15 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.statusResponse'
$ref: '#/definitions/handlers.StatusResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
security:
- BearerAuth: []
OrgSlugAuth: []
@@ -231,26 +231,26 @@ paths:
name: body
required: true
schema:
$ref: '#/definitions/handlers.updateScheduleRequest'
$ref: '#/definitions/handlers.UpdateScheduleRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.scheduleResponse'
$ref: '#/definitions/handlers.ScheduleResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
security:
- BearerAuth: []
OrgSlugAuth: []
@@ -277,12 +277,12 @@ paths:
description: OK
schema:
items:
$ref: '#/definitions/handlers.historyEntry'
$ref: '#/definitions/handlers.HistoryEntry'
type: array
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
security:
- BearerAuth: []
OrgSlugAuth: []
@@ -308,15 +308,15 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.runNowResponse'
$ref: '#/definitions/handlers.RunNowResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/handlers.errorResponse'
$ref: '#/definitions/handlers.ErrorResponse'
security:
- BearerAuth: []
OrgSlugAuth: []
+42 -42
View File
@@ -15,32 +15,32 @@ import (
"github.com/Molecule-AI/molecule-monorepo/platform/internal/scheduler"
)
// errorResponse is returned for 4xx/5xx errors. (OpenAPI doc shape — used by swaggo.)
type errorResponse struct {
// ErrorResponse is returned for 4xx/5xx errors. (OpenAPI doc shape — used by swaggo.)
type ErrorResponse struct {
Error string `json:"error"`
}
// statusResponse is returned by mutating endpoints that only echo a status verb.
type statusResponse struct {
// StatusResponse is returned by mutating endpoints that only echo a status verb.
type StatusResponse struct {
Status string `json:"status"`
}
// createScheduleResponse is returned by POST /workspaces/{id}/schedules.
type createScheduleResponse struct {
// CreateScheduleResponse is returned by POST /workspaces/{id}/schedules.
type CreateScheduleResponse struct {
ID string `json:"id"`
Status string `json:"status"`
NextRunAt time.Time `json:"next_run_at"`
}
// runNowResponse is returned by POST /workspaces/{id}/schedules/{scheduleId}/run.
type runNowResponse struct {
// RunNowResponse is returned by POST /workspaces/{id}/schedules/{scheduleId}/run.
type RunNowResponse struct {
Status string `json:"status"`
WorkspaceID string `json:"workspace_id"`
Prompt string `json:"prompt"`
}
// historyEntry is one row of /workspaces/{id}/schedules/{scheduleId}/history.
type historyEntry struct {
// HistoryEntry is one row of /workspaces/{id}/schedules/{scheduleId}/history.
type HistoryEntry struct {
Timestamp time.Time `json:"timestamp"`
DurationMs *int `json:"duration_ms"`
Status *string `json:"status"`
@@ -54,7 +54,7 @@ func NewScheduleHandler() *ScheduleHandler {
return &ScheduleHandler{}
}
type scheduleResponse struct {
type ScheduleResponse struct {
ID string `json:"id"`
WorkspaceID string `json:"workspace_id"`
Name string `json:"name"`
@@ -78,8 +78,8 @@ type scheduleResponse struct {
// @Tags schedules
// @Produce json
// @Param id path string true "Workspace ID"
// @Success 200 {array} scheduleResponse
// @Failure 500 {object} errorResponse
// @Success 200 {array} ScheduleResponse
// @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules [get]
// @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) List(c *gin.Context) {
@@ -100,9 +100,9 @@ func (h *ScheduleHandler) List(c *gin.Context) {
}
defer rows.Close()
schedules := make([]scheduleResponse, 0)
schedules := make([]ScheduleResponse, 0)
for rows.Next() {
var s scheduleResponse
var s ScheduleResponse
if err := rows.Scan(
&s.ID, &s.WorkspaceID, &s.Name, &s.CronExpr, &s.Timezone,
&s.Prompt, &s.Enabled, &s.LastRunAt, &s.NextRunAt, &s.RunCount,
@@ -120,7 +120,7 @@ func (h *ScheduleHandler) List(c *gin.Context) {
c.JSON(http.StatusOK, schedules)
}
type createScheduleRequest struct {
type CreateScheduleRequest struct {
Name string `json:"name"`
CronExpr string `json:"cron_expr" binding:"required"`
Timezone string `json:"timezone"`
@@ -135,17 +135,17 @@ type createScheduleRequest struct {
// @Accept json
// @Produce json
// @Param id path string true "Workspace ID"
// @Param body body createScheduleRequest true "Schedule fields"
// @Success 201 {object} createScheduleResponse
// @Failure 400 {object} errorResponse
// @Failure 500 {object} errorResponse
// @Param body body CreateScheduleRequest true "Schedule fields"
// @Success 201 {object} CreateScheduleResponse
// @Failure 400 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules [post]
// @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) Create(c *gin.Context) {
workspaceID := c.Param("id")
ctx := c.Request.Context()
var body createScheduleRequest
var body CreateScheduleRequest
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "cron_expr and prompt are required"})
return
@@ -199,7 +199,7 @@ func (h *ScheduleHandler) Create(c *gin.Context) {
})
}
type updateScheduleRequest struct {
type UpdateScheduleRequest struct {
Name *string `json:"name"`
CronExpr *string `json:"cron_expr"`
Timezone *string `json:"timezone"`
@@ -216,11 +216,11 @@ type updateScheduleRequest struct {
// @Produce json
// @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID"
// @Param body body updateScheduleRequest true "Partial schedule fields (only provided keys are updated)"
// @Success 200 {object} scheduleResponse
// @Failure 400 {object} errorResponse
// @Failure 404 {object} errorResponse
// @Failure 500 {object} errorResponse
// @Param body body UpdateScheduleRequest true "Partial schedule fields (only provided keys are updated)"
// @Success 200 {object} ScheduleResponse
// @Failure 400 {object} ErrorResponse
// @Failure 404 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId} [patch]
// @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) Update(c *gin.Context) {
@@ -228,7 +228,7 @@ func (h *ScheduleHandler) Update(c *gin.Context) {
workspaceID := c.Param("id") // #113: bind to owning workspace to prevent IDOR
ctx := c.Request.Context()
var body updateScheduleRequest
var body UpdateScheduleRequest
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid JSON"})
return
@@ -304,9 +304,9 @@ func (h *ScheduleHandler) Update(c *gin.Context) {
// @Produce json
// @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID"
// @Success 200 {object} statusResponse
// @Failure 404 {object} errorResponse
// @Failure 500 {object} errorResponse
// @Success 200 {object} StatusResponse
// @Failure 404 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId} [delete]
// @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) Delete(c *gin.Context) {
@@ -337,9 +337,9 @@ func (h *ScheduleHandler) Delete(c *gin.Context) {
// @Produce json
// @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID"
// @Success 200 {object} runNowResponse
// @Failure 404 {object} errorResponse
// @Failure 500 {object} errorResponse
// @Success 200 {object} RunNowResponse
// @Failure 404 {object} ErrorResponse
// @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId}/run [post]
// @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) RunNow(c *gin.Context) {
@@ -378,8 +378,8 @@ func (h *ScheduleHandler) RunNow(c *gin.Context) {
// @Produce json
// @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID"
// @Success 200 {array} historyEntry
// @Failure 500 {object} errorResponse
// @Success 200 {array} HistoryEntry
// @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId}/history [get]
// @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) History(c *gin.Context) {
@@ -407,9 +407,9 @@ func (h *ScheduleHandler) History(c *gin.Context) {
}
defer rows.Close()
entries := make([]historyEntry, 0)
entries := make([]HistoryEntry, 0)
for rows.Next() {
var e historyEntry
var e HistoryEntry
var reqStr string
if err := rows.Scan(&e.Timestamp, &e.DurationMs, &e.Status, &e.ErrorDetail, &reqStr); err != nil {
continue
@@ -421,11 +421,11 @@ func (h *ScheduleHandler) History(c *gin.Context) {
c.JSON(http.StatusOK, entries)
}
// scheduleHealthResponse is the read-only health view of a schedule.
// ScheduleHealthResponse is the read-only health view of a schedule.
// It deliberately omits prompt and cron_expr so sensitive task content is
// never exposed to peer workspaces — only execution-state fields needed to
// detect silent cron failures are returned (issue #249).
type scheduleHealthResponse struct {
type ScheduleHealthResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
@@ -494,9 +494,9 @@ func (h *ScheduleHandler) Health(c *gin.Context) {
}
defer rows.Close()
schedules := make([]scheduleHealthResponse, 0)
schedules := make([]ScheduleHealthResponse, 0)
for rows.Next() {
var s scheduleHealthResponse
var s ScheduleHealthResponse
if err := rows.Scan(
&s.ID, &s.Name, &s.Enabled, &s.LastRunAt, &s.NextRunAt,
&s.RunCount, &s.LastStatus, &s.LastError,
@@ -234,7 +234,7 @@ func TestScheduleHealth_SelfCall_Allowed(t *testing.T) {
t.Fatalf("expected 200 for self-call, got %d: %s", w.Code, w.Body.String())
}
var resp []scheduleHealthResponse
var resp []ScheduleHealthResponse
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
@@ -284,7 +284,7 @@ func TestScheduleHealth_CanCommunicatePeer_LegacyNoToken(t *testing.T) {
t.Fatalf("expected 200 for peer with no tokens, got %d: %s", w.Code, w.Body.String())
}
var resp []scheduleHealthResponse
var resp []ScheduleHealthResponse
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("failed to parse response: %v", err)
}