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.
Part of #10 — Go HTTP Daemon lane. Sub-issue of umbrella #18.
Depends on
Goal
All 7 canonical project routes mounted under
/api/v1with the chi REST timeout group from the skeleton. Each handler returns501+{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 inplanned.legacy+ handler comments, instead of registering aliases.PUT /projects/:idaliased toPATCHPOST /projects/:idoverloaded for repairPOST /projects/{id}/repair. Legacy path not registered.GET /:idreturns 200 witherrorfield{project, status:"ok"|"degraded"}.ok/successflags{error: msg}{error, code, message, requestId, details?}.Route surface (7 canonical)
/api/v1/projects200 {projects: ProjectSummary[]}/api/v1/projects201 {project}.400 INVALID_JSON|PATH_REQUIRED|NOT_A_GIT_REPO,409 PATH_ALREADY_REGISTERED|ID_ALREADY_REGISTEREDwithdetails.existingProjectId,suggestedProjectId./api/v1/projects/{id}200 {project, status:"ok"|"degraded"},404 PROJECT_NOT_FOUND./api/v1/projects/{id}200 {project}.400 INVALID_JSON|IDENTITY_FROZEN|INVALID_LOCAL_CONFIG,404,409 PROJECT_DEGRADED|MISSING_PATH./api/v1/projects/{id}200 {projectId, removedStorageDir}.400 INVALID_PROJECT_ID,404./api/v1/projects/{id}/repair200 {project}.400 PROJECT_NOT_DEGRADED|REPAIR_NOT_AVAILABLE,404. planned.legacy: ["POST /api/v1/projects/{id}"] (R4)./api/v1/projects/reload200 {reloaded:true, projectCount, degradedCount}.chi gotcha: register
/projects/reloadBEFORE/projects/{id}for POST so reload isn't captured by the wildcard.Files this PR introduces (shared scaffolding lands here, reused by #18b/c)
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
Out of scope
ProjectServiceimplementation.Closes part of #18.