Repository navigation
Description
Part of #10 — Go HTTP Daemon lane. Sub-issue of umbrella #18.
⚠️ Scope: Interface-first. Register 7 canonical project routes as501 Not Implementedwith a structuredplannedbody. No business logic. No realProjectServiceimpl.
Depends on
- feat(backend): HTTP daemon skeleton (Phase 1a) — #10 #14 (Skeleton PR for Tracking: Go HTTP Daemon — REST #10·1a) must be merged first.
- Sibling: #18b (Sessions), #18c (Issues+rest), feat(api): 1c — OpenAPI 3.1 spec + request/response validation #19 (OpenAPI follow-up).
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 sanityOut of scope
- Real
ProjectServiceimplementation. - TS-side compat shims, alias paths.
- OpenAPI spec authoring → feat(api): 1c — OpenAPI 3.1 spec + request/response validation #19.
- Frontend updates to call canonical paths → separate PR in old orchestrator repo.
Closes part of #18.
Activity
refactor(api): collapse ProjectService → ProjectManager — #20
refactor(api): replace stubs/ with OpenAPI-as-source-of-truth — #20
feat(api): projects route shell (7 routes, REST-corrected) — #20
refactor(api): collapse ProjectService → ProjectManager — #20
refactor(api): replace stubs/ with OpenAPI-as-source-of-truth — #20
refactor(api): move projects contract to internal/project package — #20
refactor(api): consolidate project types into internal/project — #20
feat(api): projects route shell (7 routes, REST-corrected) — #20
refactor(api): collapse ProjectService → ProjectManager — #20
refactor(api): replace stubs/ with OpenAPI-as-source-of-truth — #20
refactor(api): move projects contract to internal/project package — #20
refactor(api): consolidate project types into internal/project — #20
feat(api): implement project routes with mock manager/store (#47)
feat(api): code-first OpenAPI generation + typed frontend client
feat(api): code-first OpenAPI generation + typed frontend client