From a10b385841265618c6c554af2f32fd96a221f5fd Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 16 Jun 2026 17:58:47 +0000 Subject: [PATCH 1/2] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/Payel-git-ol/Octra/issues/87 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..a4ace40 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-06-16T17:58:47.865Z for PR creation at branch issue-87-84fa7bc45fb8 for issue https://github.com/Payel-git-ol/Octra/issues/87 \ No newline at end of file From be228bb9ba254791a0f2b316ebe0d51fbb7692a2 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 16 Jun 2026 18:07:28 +0000 Subject: [PATCH 2/2] fix: custom workflow now passes the real task to workers (#87) Custom workflows built in the canvas (Boss -> Managers -> Workers) ran all agents but produced no code. Root cause: decisionFromPredefinedWorkflow set TechnicalDescription to the architecture label ('Custom workflow with Boss and N managers') instead of the user's request. Since that string becomes each worker's TASK.md, workers were told to implement a meaningless label and generated nothing. - TechnicalDescription now carries the real task (title + description); the architecture label stays in ArchitectureNotes for Boss context. - Detect the tech stack from the task when the custom workflow omits it, mirroring the AI-planning path (no more silent Go default). - Fix hasRealCode regression (commit 6f5da02): the '>= 3 lines' threshold rejected valid short source (e.g. minimal main.go), breaking TestContainsSourceCode and orchestrator CI; require >= 1 real code line. Updates TestDecisionFromPredefinedWorkflow and adds a regression test. --- .../internal/service/rules/boss/plan.go | 24 ++++++- .../internal/service/rules/boss/plan_test.go | 65 ++++++++++++++++++- .../service/rules/worker/tool_executor.go | 8 ++- 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/orchestrator/internal/service/rules/boss/plan.go b/orchestrator/internal/service/rules/boss/plan.go index 9f6262c..603ce7e 100644 --- a/orchestrator/internal/service/rules/boss/plan.go +++ b/orchestrator/internal/service/rules/boss/plan.go @@ -151,13 +151,33 @@ func decisionFromPredefinedWorkflow(req *CreateTaskRequest) *DecisionResult { architecture = "User-defined workflow" } + // The technical description becomes each worker's TASK.md, so it MUST carry + // the real user request — not the workflow label. Previously this used the + // architecture string ("Custom workflow with Boss and N managers"), so every + // worker was told to implement a meaningless label instead of the actual task + // and produced no code (issue #87). The architecture stays in ArchitectureNotes + // for the Boss validation/context. + technical := strings.TrimSpace(req.Title + "\n" + req.Description) + if technical == "" { + technical = architecture + } + + // Honour the user-provided tech stack, but when the custom workflow omits it + // (e.g. the Boss node has no stack configured) fall back to keyword detection + // so workers don't silently default to Go for, say, a Python task — mirroring + // the AI-planning path in thinkOnce. + techStack := append([]string(nil), req.PredefinedTechStack...) + if len(techStack) == 0 { + techStack = detectTechStack(req.Title, req.Description) + } + return &DecisionResult{ TaskType: taskType, ManagersCount: int32(len(managerRoles)), ManagerRoles: managerRoles, ManagerWorkflows: req.PredefinedManagers, - TechnicalDescription: firstNonEmpty(architecture, req.Title+"\n"+req.Description), - TechStack: append([]string(nil), req.PredefinedTechStack...), + TechnicalDescription: technical, + TechStack: techStack, ArchitectureNotes: architecture, } } diff --git a/orchestrator/internal/service/rules/boss/plan_test.go b/orchestrator/internal/service/rules/boss/plan_test.go index bc64cd6..8743536 100644 --- a/orchestrator/internal/service/rules/boss/plan_test.go +++ b/orchestrator/internal/service/rules/boss/plan_test.go @@ -1,6 +1,9 @@ package boss -import "testing" +import ( + "strings" + "testing" +) func TestDecisionFromPredefinedWorkflow(t *testing.T) { req := &CreateTaskRequest{ @@ -34,14 +37,70 @@ func TestDecisionFromPredefinedWorkflow(t *testing.T) { if got := len(predefinedWorkersFor(decision, 0, "Research Lead")); got != 1 { t.Fatalf("predefined worker count = %d, want 1", got) } - if decision.TechnicalDescription != "Custom research workflow" { - t.Fatalf("technical description = %q", decision.TechnicalDescription) + // The technical description must be the real task (what workers implement), + // not the workflow label. The architecture label lives in ArchitectureNotes. + if !strings.Contains(decision.TechnicalDescription, "Research quarterly market data") || + !strings.Contains(decision.TechnicalDescription, "Find recent sources and produce a short brief") { + t.Fatalf("technical description = %q, want it to contain the real task", decision.TechnicalDescription) + } + if decision.ArchitectureNotes != "Custom research workflow" { + t.Fatalf("architecture notes = %q, want %q", decision.ArchitectureNotes, "Custom research workflow") } if len(decision.TechStack) != 1 || decision.TechStack[0] != "web search" { t.Fatalf("tech stack = %#v, want [web search]", decision.TechStack) } } +// TestDecisionFromPredefinedWorkflowCarriesRealTask is the regression test for +// issue #87: a custom workflow built in the canvas (Boss → Managers → Workers) +// must hand workers the actual user request, otherwise they get only the +// "Custom workflow with ... managers" label and produce no code. +func TestDecisionFromPredefinedWorkflowCarriesRealTask(t *testing.T) { + req := &CreateTaskRequest{ + Title: "Build a proxy server", + Description: "Write a reverse proxy in Python with load balancing", + // Architecture is the auto-generated label sent by the frontend canvas. + PredefinedArchitecture: "Custom workflow with Architect and 2 managers", + // No tech stack set on the Boss node — must be detected from the task. + PredefinedManagers: []ManagerWorkflow{ + { + Role: "Backend", + Description: "Backend manager", + Priority: 1, + Workers: []WorkerWorkflow{ + {Role: "Developer", Description: "Developer worker"}, + }, + }, + { + Role: "Coordinator", + Description: "Coordinator manager", + Priority: 2, + Workers: []WorkerWorkflow{ + {Role: "Developer", Description: "Developer worker"}, + {Role: "Designer", Description: "Designer worker"}, + }, + }, + }, + } + + decision := decisionFromPredefinedWorkflow(req) + + if strings.Contains(decision.TechnicalDescription, "Custom workflow with") { + t.Fatalf("technical description leaked the workflow label: %q", decision.TechnicalDescription) + } + if !strings.Contains(decision.TechnicalDescription, "Build a proxy server") || + !strings.Contains(decision.TechnicalDescription, "reverse proxy in Python") { + t.Fatalf("technical description lost the real task: %q", decision.TechnicalDescription) + } + if decision.ManagersCount != 2 { + t.Fatalf("managers count = %d, want 2", decision.ManagersCount) + } + // Tech stack must be detected from the request when the workflow omits it. + if len(decision.TechStack) == 0 || decision.TechStack[0] != "python" { + t.Fatalf("tech stack = %#v, want python detected from the task", decision.TechStack) + } +} + func TestFallbackManagerRoleMatchesTaskType(t *testing.T) { cases := []struct { taskType string diff --git a/orchestrator/internal/service/rules/worker/tool_executor.go b/orchestrator/internal/service/rules/worker/tool_executor.go index ed8a95f..538527f 100644 --- a/orchestrator/internal/service/rules/worker/tool_executor.go +++ b/orchestrator/internal/service/rules/worker/tool_executor.go @@ -351,8 +351,12 @@ func hasRealCode(content string) bool { } nonStubLines++ } - // В файле должно быть хотя бы 3 строки реального кода (не комментариев) - return nonStubLines >= 3 + // Файл считается реальным кодом, если в нём есть хотя бы одна строка кода, + // а не только пустые строки и комментарии/TODO-заглушки (issue #98). Порог + // «>= 3 строки» был ошибкой: он отбраковывал валидный короткий код вроде + // `package main` + `func main() {}` или однострочного express-сервера, из-за + // чего такой код не считался исходником (см. TestContainsSourceCode, issue #75). + return nonStubLines >= 1 } // containsSourceCode сообщает, есть ли среди файлов хотя бы один непустой файл с