From 940a5bab031bfe81cea9c90d64e6ebc804c366f9 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 4 Apr 2026 09:30:13 +0000 Subject: feat: story ship gate — explicit POST /api/stories/{id}/ship; remove auto-deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - checkStoryCompletion now guards against re-running on already-SHIPPABLE stories and no longer auto-triggers triggerStoryDeploy on completion - New Pool.ShipStory method validates SHIPPABLE state then fires triggerStoryDeploy - POST /api/stories/{id}/ship route registered and handleShipStory handler added - Two new tests: 202 for SHIPPABLE story, 409 for non-SHIPPABLE story Co-Authored-By: Claude Sonnet 4.6 --- internal/api/stories.go | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'internal/api/stories.go') diff --git a/internal/api/stories.go b/internal/api/stories.go index 629ea7a..1743dbe 100644 --- a/internal/api/stories.go +++ b/internal/api/stories.go @@ -322,6 +322,17 @@ func (s *Server) handleApproveStory(w http.ResponseWriter, r *http.Request) { }) } +// handleShipStory triggers the merge + deploy for a SHIPPABLE story. +// POST /api/stories/{id}/ship +func (s *Server) handleShipStory(w http.ResponseWriter, r *http.Request) { + id := r.PathValue("id") + if err := s.pool.ShipStory(r.Context(), id); err != nil { + writeJSON(w, http.StatusConflict, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusAccepted, map[string]string{"message": "story shipping initiated", "story_id": id}) +} + // handleStoryDeploymentStatus aggregates the deployment status across all tasks in a story. // GET /api/stories/{id}/deployment-status func (s *Server) handleStoryDeploymentStatus(w http.ResponseWriter, r *http.Request) { -- cgit v1.2.3