diff options
| author | Claudomator Agent <agent@claudomator> | 2026-06-05 09:21:32 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-06-05 09:21:32 +0000 |
| commit | 0eb0b79396663c4901597becc6857a4cd795c58e (patch) | |
| tree | 3532c82aa3f7c6deeed03f143d1c360d7cf6cc47 /internal/api/elaborate.go | |
| parent | fa3ed5220a28f326d443726233c37e1a7df0ced5 (diff) | |
chore: remove stories/checker backend dead code
Remove the dead stories/checker backend tracked as design debt in CLAUDE.md:
- Delete internal/api/stories.go, stories_test.go, elaborate.go
- Delete internal/task/story.go, story_test.go
- Remove story/checker columns from storage schema and all CRUD methods
- Remove checkStoryCompletion, spawnCheckerTask, triggerStoryDeploy,
createValidationTask, checkValidationResult, ShipStory, CheckStoryCompletion
from executor; simplify handleRunResult
- Remove story/checker fields from task.Task (StoryID, AcceptanceCriteria,
CheckerForTaskID, CheckerReport)
- Remove story routes and geminiBinPath from API server
- Move claudeJSONResult/extractJSON from elaborate.go to validate.go
- Rename ensureStoryBranch -> ensureBranch in ContainerRunner
- Fix git identity in bare-repo tests; fix initial-branch to use main
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/api/elaborate.go')
| -rw-r--r-- | internal/api/elaborate.go | 244 |
1 files changed, 0 insertions, 244 deletions
diff --git a/internal/api/elaborate.go b/internal/api/elaborate.go deleted file mode 100644 index e8930a6..0000000 --- a/internal/api/elaborate.go +++ /dev/null @@ -1,244 +0,0 @@ -package api - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "os/exec" - "strings" - "time" -) - -const elaborateTimeout = 30 * time.Second - -// claudeJSONResult is the top-level object returned by `claude --output-format json`. -type claudeJSONResult struct { - Result string `json:"result"` - IsError bool `json:"is_error"` -} - -// geminiJSONResult is the top-level object returned by `gemini --output-format json`. -type geminiJSONResult struct { - Response string `json:"response"` -} - -// extractJSON returns the first top-level JSON object found in s, stripping -// surrounding prose or markdown code fences the model may have added. -func extractJSON(s string) string { - start := strings.Index(s, "{") - end := strings.LastIndex(s, "}") - if start == -1 || end == -1 || end < start { - return s - } - return s[start : end+1] -} - -func (s *Server) claudeBinaryPath() string { - if s.elaborateCmdPath != "" { - return s.elaborateCmdPath - } - if s.claudeBinPath != "" { - return s.claudeBinPath - } - return "claude" -} - -func (s *Server) geminiBinaryPath() string { - if s.geminiBinPath != "" { - return s.geminiBinPath - } - return "gemini" -} - -// elaboratedStorySubtask is a leaf unit within a story task. -type elaboratedStorySubtask struct { - Name string `json:"name"` - Instructions string `json:"instructions"` -} - -// elaboratedStoryTask is one independently-deployable unit in a story plan. -type elaboratedStoryTask struct { - Name string `json:"name"` - Instructions string `json:"instructions"` - AcceptanceCriteria string `json:"acceptance_criteria"` - Subtasks []elaboratedStorySubtask `json:"subtasks"` -} - -// elaboratedStoryValidation describes how to verify the story was successful. -type elaboratedStoryValidation struct { - Type string `json:"type"` - Steps []string `json:"steps"` - SuccessCriteria string `json:"success_criteria"` -} - -// elaboratedStory is the full implementation plan produced by story elaboration. -type elaboratedStory struct { - Name string `json:"name"` - BranchName string `json:"branch_name"` - Tasks []elaboratedStoryTask `json:"tasks"` - Validation elaboratedStoryValidation `json:"validation"` -} - -func buildStoryElaboratePrompt() string { - return `You are a software architect. Given a goal, analyze the codebase at /workspace and produce a structured implementation plan as JSON. - -Output ONLY valid JSON matching this schema: -{ - "name": "story name", - "branch_name": "story/kebab-case-name", - "tasks": [ - { - "name": "task name", - "instructions": "detailed instructions including file paths and what to change", - "acceptance_criteria": "specific, verifiable conditions a separate reviewer can check — e.g. 'run go test ./... and verify all pass; confirm GET /api/foo returns 200 with expected JSON shape'", - "subtasks": [ - { "name": "subtask name", "instructions": "..." } - ] - } - ], - "validation": { - "type": "build|test|smoke", - "steps": ["step1", "step2"], - "success_criteria": "what success looks like" - } -} - -Rules: -- Tasks must be independently buildable (each can be deployed alone) -- Subtasks within a task are order-dependent and run sequentially -- Instructions must include specific file paths, function names, and exact changes -- Instructions must end with: git add -A && git commit -m "..." && git push origin <branch> -- Validation should match the scope: small change = build check; new feature = smoke test -- acceptance_criteria must be concrete and verifiable by a separate agent — no vague assertions like "code looks good"` -} - -func (s *Server) elaborateStoryWithClaude(ctx context.Context, workDir, goal string) (*elaboratedStory, error) { - cmd := exec.CommandContext(ctx, s.claudeBinaryPath(), - "-p", goal, - "--system-prompt", buildStoryElaboratePrompt(), - "--output-format", "json", - "--model", "haiku", - ) - if workDir != "" { - cmd.Dir = workDir - } - - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - - err := cmd.Run() - - output := stdout.Bytes() - if len(output) == 0 { - if err != nil { - return nil, fmt.Errorf("claude failed: %w (stderr: %s)", err, stderr.String()) - } - return nil, fmt.Errorf("claude returned no output") - } - - var wrapper claudeJSONResult - if jerr := json.Unmarshal(output, &wrapper); jerr != nil { - return nil, fmt.Errorf("failed to parse claude JSON wrapper: %w (output: %s)", jerr, string(output)) - } - if wrapper.IsError { - return nil, fmt.Errorf("claude error: %s", wrapper.Result) - } - - var result elaboratedStory - if jerr := json.Unmarshal([]byte(extractJSON(wrapper.Result)), &result); jerr != nil { - return nil, fmt.Errorf("failed to parse elaborated story JSON: %w (result: %s)", jerr, wrapper.Result) - } - return &result, nil -} - -func (s *Server) elaborateStoryWithGemini(ctx context.Context, workDir, goal string) (*elaboratedStory, error) { - combinedPrompt := fmt.Sprintf("%s\n\n%s", buildStoryElaboratePrompt(), goal) - cmd := exec.CommandContext(ctx, s.geminiBinaryPath(), - "-p", combinedPrompt, - "--output-format", "json", - "--model", "gemini-2.5-flash-lite", - ) - if workDir != "" { - cmd.Dir = workDir - } - - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - - if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("gemini failed: %w (stderr: %s)", err, stderr.String()) - } - - var wrapper geminiJSONResult - if err := json.Unmarshal(stdout.Bytes(), &wrapper); err != nil { - return nil, fmt.Errorf("failed to parse gemini JSON wrapper: %w (output: %s)", err, stdout.String()) - } - - var result elaboratedStory - if err := json.Unmarshal([]byte(extractJSON(wrapper.Response)), &result); err != nil { - return nil, fmt.Errorf("failed to parse elaborated story JSON: %w (response: %s)", err, wrapper.Response) - } - return &result, nil -} - -func (s *Server) handleElaborateStory(w http.ResponseWriter, r *http.Request) { - var input struct { - Goal string `json:"goal"` - ProjectID string `json:"project_id"` - } - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { - writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()}) - return - } - if input.Goal == "" { - writeJSON(w, http.StatusBadRequest, map[string]string{"error": "goal is required"}) - return - } - if input.ProjectID == "" { - writeJSON(w, http.StatusBadRequest, map[string]string{"error": "project_id is required"}) - return - } - - proj, err := s.store.GetProject(input.ProjectID) - if err != nil { - writeJSON(w, http.StatusNotFound, map[string]string{"error": "project not found"}) - return - } - - // Update git refs without modifying the working tree. - if proj.LocalPath != "" { - gitCmd := exec.Command("git", "-C", proj.LocalPath, "fetch", "origin") - if err := gitCmd.Run(); err != nil { - s.logger.Warn("story elaborate: git fetch failed", "error", err, "path", proj.LocalPath) - } - } - - ctx, cancel := context.WithTimeout(r.Context(), elaborateTimeout) - defer cancel() - - result, err := s.elaborateStoryWithClaude(ctx, proj.LocalPath, input.Goal) - if err != nil { - s.logger.Warn("story elaborate: claude failed, falling back to gemini", "error", err) - result, err = s.elaborateStoryWithGemini(ctx, proj.LocalPath, input.Goal) - if err != nil { - s.logger.Error("story elaborate: fallback gemini also failed", "error", err) - writeJSON(w, http.StatusBadGateway, map[string]string{ - "error": fmt.Sprintf("elaboration failed: %v", err), - }) - return - } - } - - if result.Name == "" { - writeJSON(w, http.StatusBadGateway, map[string]string{ - "error": "elaboration failed: missing required fields in response", - }) - return - } - - writeJSON(w, http.StatusOK, result) -} |
