From 392c7c1ada310b2f928dca89b75ba628478f7694 Mon Sep 17 00:00:00 2001 From: Claude Sonnet 5 Date: Fri, 3 Jul 2026 23:46:26 +0000 Subject: feat(story): add Epic/Story data model + REST CRUD (Phase 7a) Pure data model + CRUD -- no orchestration behavior yet (that's Phase 7b). Revives ADR-007's stories concept (adapted; ADR-007 itself was deleted as "more machinery than the usage pattern needed") while staying lean: reuses the existing events/projects/task-tree machinery rather than building a parallel hierarchy. - internal/story: Epic/Story types. Epic is a loosely-scoped initiative (OPEN/CLOSED, no execution semantics). Story is a shippable slice of work realized by a task tree rooted at root_task_id; epic_id/project_id/ root_task_id are loose references (no FK enforcement, matching the codebase's existing tolerance for unmatched reference strings like tasks.project). - storage: new epics/stories tables (additive migrations) + CRUD methods. Story status is intentionally unvalidated pass-through in this phase -- no task.ValidTransition-style state machine, since there's no orchestrator yet to make transitions meaningful. - event: 9 new Kind constants for the ceremony this layer will eventually drive (epic_proposed, discovery_proposed, framing_decided, groomed, prioritized, eval_verdict, arbitration_decided, retro_captured, human_accepted) -- defined but nothing emits them yet. - api: GET/POST /api/epics, GET/PUT /api/epics/{id}, GET /api/epics/{id}/stories, GET/POST /api/stories, GET/PUT /api/stories/{id}, and GET /api/stories/{id}/task-tree -- BFS walk from root_task_id following both parent_task_id children and depends_on-edge dependents (visited-set guarded), returning a flat node list for a later UI phase to render as a graph. Unauthenticated, matching the existing projects/tasks endpoints' posture. go build/vet/test -race -count=1 all pass, full suite. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01V1moSNCJRcP6kykA4tyUSs --- internal/api/server.go | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'internal/api/server.go') diff --git a/internal/api/server.go b/internal/api/server.go index 0e5b185..9f0eb7c 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -172,6 +172,16 @@ func (s *Server) routes() { s.mux.HandleFunc("POST /api/projects", s.handleCreateProject) s.mux.HandleFunc("GET /api/projects/{id}", s.handleGetProject) s.mux.HandleFunc("PUT /api/projects/{id}", s.handleUpdateProject) + s.mux.HandleFunc("GET /api/epics", s.handleListEpics) + s.mux.HandleFunc("POST /api/epics", s.handleCreateEpic) + s.mux.HandleFunc("GET /api/epics/{id}", s.handleGetEpic) + s.mux.HandleFunc("PUT /api/epics/{id}", s.handleUpdateEpic) + s.mux.HandleFunc("GET /api/epics/{id}/stories", s.handleListEpicStories) + s.mux.HandleFunc("GET /api/stories", s.handleListStories) + s.mux.HandleFunc("POST /api/stories", s.handleCreateStory) + s.mux.HandleFunc("GET /api/stories/{id}", s.handleGetStory) + s.mux.HandleFunc("PUT /api/stories/{id}", s.handleUpdateStory) + s.mux.HandleFunc("GET /api/stories/{id}/task-tree", s.handleStoryTaskTree) s.mux.HandleFunc("POST /api/roles/{role}/versions", s.handleCreateRoleVersion) s.mux.HandleFunc("GET /api/roles/{role}/versions", s.handleListRoleVersions) s.mux.HandleFunc("POST /api/roles/{role}/activate", s.handleActivateRoleVersion) -- cgit v1.2.3