Pusti: premešta task ready→active bez pokretanja claude sesije

- handleRunTask samo premešta task iz ready/ u active/ sa timestampom
- Uklonjena zavisnost od console sesija — konzola je nezavisna
- Korisnik pokreće claude ručno iz konzole terminala
- Ažurirani testovi (6 RunTask testova prolaze)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
djuka 2026-02-21 04:09:30 +00:00
parent 932ffe5203
commit ac72ca6f52
2 changed files with 29 additions and 64 deletions

View File

@ -123,6 +123,9 @@ func (s *Server) setupRoutes() {
s.Router.GET("/console/history/:session", s.handleConsoleHistory)
s.Router.GET("/console/ws/:session", s.handleConsoleWS)
// Logs route
s.Router.GET("/api/logs/tail", s.handleLogsTail)
// Docs routes
s.Router.GET("/docs", s.handleDocsList)
s.Router.GET("/docs/*path", s.handleDocsView)
@ -346,54 +349,19 @@ func (s *Server) handleRunTask(c *gin.Context) {
task.Status = "ready"
}
// Find free session
sessionIdx := -1
for i := 0; i < 2; i++ {
sess := s.console.getSession(i)
sess.mu.Lock()
if sess.status == "idle" {
sessionIdx = i
sess.mu.Unlock()
break
}
sess.mu.Unlock()
}
if sessionIdx == -1 {
c.JSON(http.StatusConflict, gin.H{"error": "obe sesije su zauzete"})
// Move ready → active
if err := supervisor.MoveTask(s.Config.TasksDir, id, "ready", "active"); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Build the prompt
prompt := "Pročitaj CLAUDE.md u root-u projekta. Tvoj task: TASKS/ready/" + id + ".md — Pročitaj task fajl i uradi šta piše. Prati pravila iz CLAUDE.md."
// Start in the session
session := s.console.getSession(sessionIdx)
execID := s.console.nextExecID()
session.mu.Lock()
session.status = "running"
session.execID = execID
session.taskID = id
session.output = nil
session.history = append(session.history, historyEntry{
Command: "pusti " + id,
ExecID: execID,
Timestamp: timeNow(),
Status: "running",
})
session.mu.Unlock()
// Append "Pokrenut" timestamp
taskPath := filepath.Join(s.Config.TasksDir, "ready", id+".md")
appendTimestamp(taskPath, "Pokrenut")
go s.runCommand(session, prompt, execID)
taskPath := filepath.Join(s.Config.TasksDir, "active", id+".md")
appendTimestamp(taskPath, "Pokrenut (→active)")
c.JSON(http.StatusOK, gin.H{
"status": "started",
"session": sessionIdx + 1,
"exec_id": execID,
"task": id,
})
}

View File

@ -809,8 +809,12 @@ func TestRunTask_Ready(t *testing.T) {
if resp["status"] != "started" {
t.Errorf("expected status started, got %v", resp["status"])
}
if resp["session"] == nil {
t.Error("expected session number in response")
if resp["task"] != "T08" {
t.Errorf("expected task T08, got %v", resp["task"])
}
// Verify task moved to active/
if _, err := os.Stat(filepath.Join(srv.Config.TasksDir, "active", "T08.md")); err != nil {
t.Error("expected T08.md in active/")
}
}
@ -853,7 +857,7 @@ func TestRunTask_NotFound(t *testing.T) {
}
}
func TestRunTask_BothSessionsBusy(t *testing.T) {
func TestRunTask_MovesToActive(t *testing.T) {
srv := setupTestServer(t)
// Move T08 to ready
@ -862,30 +866,18 @@ func TestRunTask_BothSessionsBusy(t *testing.T) {
filepath.Join(srv.Config.TasksDir, "ready", "T08.md"),
)
// Occupy both sessions
srv.console.sessions[0].mu.Lock()
srv.console.sessions[0].status = "running"
srv.console.sessions[0].mu.Unlock()
srv.console.sessions[1].mu.Lock()
srv.console.sessions[1].status = "running"
srv.console.sessions[1].mu.Unlock()
req := httptest.NewRequest(http.MethodPost, "/task/T08/run", nil)
w := httptest.NewRecorder()
srv.Router.ServeHTTP(w, req)
if w.Code != http.StatusConflict {
t.Fatalf("expected 409 when both sessions busy, got %d: %s", w.Code, w.Body.String())
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
}
// Clean up
srv.console.sessions[0].mu.Lock()
srv.console.sessions[0].status = "idle"
srv.console.sessions[0].mu.Unlock()
srv.console.sessions[1].mu.Lock()
srv.console.sessions[1].status = "idle"
srv.console.sessions[1].mu.Unlock()
// Verify task moved to active/
if _, err := os.Stat(filepath.Join(srv.Config.TasksDir, "active", "T08.md")); err != nil {
t.Error("expected T08.md in active/ after run")
}
}
func TestDashboardHTML_HasRunButton(t *testing.T) {
@ -1865,11 +1857,16 @@ func TestRunTask_AddsTimestamp(t *testing.T) {
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
}
content, _ := os.ReadFile(filepath.Join(srv.Config.TasksDir, "ready", "T08.md"))
// Task should now be in active/
content, _ := os.ReadFile(filepath.Join(srv.Config.TasksDir, "active", "T08.md"))
text := string(content)
if !containsStr(text, "Pokrenut") {
t.Error("expected 'Pokrenut' timestamp after run")
}
// Verify it's no longer in ready/
if _, err := os.Stat(filepath.Join(srv.Config.TasksDir, "ready", "T08.md")); err == nil {
t.Error("task should no longer be in ready/")
}
}
// --- T24: PTY tests ---