Skip to content

feat(api): 1b·a — Projects route shell (7 routes, REST-corrected) #20

@neversettle17-101

Description

@neversettle17-101

Part of #10 — Go HTTP Daemon lane. Sub-issue of umbrella #18.

⚠️ Scope: Interface-first. Register 7 canonical project routes as 501 Not Implemented with a structured planned body. No business logic. No real ProjectService impl.

Depends on

Goal

All 7 canonical project routes mounted under /api/v1 with the chi REST timeout group from the skeleton. Each handler returns 501 + {error, code, message, requestId, planned}. Legacy TS paths are NOT registered — see REST audit below.

REST audit (applies across #18a/b/c)

Reading the old TS surface at packages/web/src/app/api/** revealed REST violations. The Go shell corrects them and documents the legacy mapping in planned.legacy + handler comments, instead of registering aliases.

# TS violation Go fix
R3 PUT /projects/:id aliased to PATCH Keep PATCH only. PUT not registered — chi returns 405.
R4 POST /projects/:id overloaded for repair Canonical POST /projects/{id}/repair. Legacy path not registered.
R5 Degraded GET /:id returns 200 with error field Discriminator {project, status:"ok"|"degraded"}.
R6 Inconsistent ok/success flags Drop on 2xx. Return affected resource.
R9 Bare {error: msg} Locked envelope {error, code, message, requestId, details?}.

Route surface (7 canonical)

Method Path Notes
GET /api/v1/projects 200 {projects: ProjectSummary[]}
POST /api/v1/projects Add. 201 {project}. 400 INVALID_JSON|PATH_REQUIRED|NOT_A_GIT_REPO, 409 PATH_ALREADY_REGISTERED|ID_ALREADY_REGISTERED with details.existingProjectId,suggestedProjectId.
GET /api/v1/projects/{id} 200 {project, status:"ok"|"degraded"}, 404 PROJECT_NOT_FOUND.
PATCH /api/v1/projects/{id} 200 {project}. 400 INVALID_JSON|IDENTITY_FROZEN|INVALID_LOCAL_CONFIG, 404, 409 PROJECT_DEGRADED|MISSING_PATH.
DELETE /api/v1/projects/{id} 200 {projectId, removedStorageDir}. 400 INVALID_PROJECT_ID, 404.
POST /api/v1/projects/{id}/repair 200 {project}. 400 PROJECT_NOT_DEGRADED|REPAIR_NOT_AVAILABLE, 404. planned.legacy: ["POST /api/v1/projects/{id}"] (R4).
POST /api/v1/projects/reload 200 {reloaded:true, projectCount, degradedCount}.

chi gotcha: register /projects/reload BEFORE /projects/{id} for POST so reload isn't captured by the wildcard.

Files this PR introduces (shared scaffolding lands here, reused by #18b/c)

backend/internal/
  domain/
    project.go            NEW
    config_types.go       NEW — Zod-ported typed config (TrackerConfig, SCMConfig,
                          AgentConfig, ReactionConfig, LocalProjectConfig, RoleAgentConfig)
                          .passthrough() parity via Extra map + custom Marshal/Unmarshal.
  ports/
    services.go           NEW — ProjectService interface (sessions API port lives in #18b)
  httpd/
    router.go             MODIFIED — mount api.Register
    api.go                NEW — API struct + /api/v1 routing
    notimpl.go            NEW — notImplemented(w, r, PlannedRoute{...})
    errors.go             NEW — writeAPIError(w, r, status, code, message, details)
    controllers/
      projects.go         NEW
      projects_test.go    NEW

Stub envelope (locked)

{
  "error":     "not_implemented",
  "code":      "NOT_IMPLEMENTED",
  "message":   "GET /api/v1/projects is registered but not yet implemented",
  "requestId": "<chi-request-id>",
  "planned": {
    "route":    "GET /api/v1/projects",
    "legacy":   [],
    "request":  { "query": {}, "body": null },
    "response": { "200": { "projects": "[]domain.ProjectSummary" } },
    "errors":   [{ "status": 500, "code": "PROJECTS_LIST_FAILED", "message": "..." }],
    "notes":    "..."
  }
}

Exit criteria

cd backend && go build ./... && go test ./internal/httpd/...
go run ./...
curl -i localhost:3001/api/v1/projects                  # 501 + envelope
curl -i -XPATCH localhost:3001/api/v1/projects/p1       # 501
curl -i -XPUT   localhost:3001/api/v1/projects/p1       # 405 (unregistered)
curl -i -XPOST  localhost:3001/api/v1/projects/p1       # 405 (legacy unregistered)
curl -i -XPOST  localhost:3001/api/v1/projects/p1/repair # 501 + planned.legacy
curl -i -XPOST  localhost:3001/api/v1/projects/reload    # 501
curl -i        localhost:3001/api/v1/missing             # 404 sanity

Out of scope


Closes part of #18.

Metadata

Metadata

Assignees

No one assigned

    Labels

    coreCore FunctionalitydaemonHTTP daemon lane

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions