From bbfbcf4f16f669839d789e3a30a6c74847544946 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 10 Apr 2026 23:21:16 +0000 Subject: [PATCH 1/4] =?UTF-8?q?docs:=20crear=20issue=200047=20=E2=80=94=20?= =?UTF-8?q?system=20prompt=20no=20se=20carga=20para=20=5Fspecials?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El runtime resuelve system_prompt_file como agents//prompts/... pero los agentes especiales viven en agents/_specials//. Esto causa que Father Bot opere sin su system prompt completo de 369 lineas. Co-Authored-By: Claude Opus 4.6 (1M context) --- dev/issues/0047-fix-system-prompt-path.md | 81 +++++++++++++++++++++++ dev/issues/README.md | 1 + 2 files changed, 82 insertions(+) create mode 100644 dev/issues/0047-fix-system-prompt-path.md diff --git a/dev/issues/0047-fix-system-prompt-path.md b/dev/issues/0047-fix-system-prompt-path.md new file mode 100644 index 0000000..a7f8ec0 --- /dev/null +++ b/dev/issues/0047-fix-system-prompt-path.md @@ -0,0 +1,81 @@ +# 0047 — System prompt no se carga para agentes en _specials/ + +## Objetivo + +El runtime resuelve la ruta del `system_prompt_file` como `agents//prompts/system.md`, +pero los agentes especiales (Father Bot, etc.) viven en `agents/_specials//`. Resultado: +el system prompt no se carga y el agente usa solo la `description` como prompt. + +## Contexto + +En `devagents/llm.go:33`, la ruta se construye asi: + +```go +spPath := filepath.Join("agents", a.cfg.Agent.ID, spFile) +``` + +Esto produce `agents/father-bot/prompts/system.md` para Father Bot, pero el archivo real esta en +`agents/_specials/father-bot/prompts/system.md`. + +Los logs confirman el problema: +```json +{"msg":"failed to load system_prompt_file, using description","path":"agents/father-bot/prompts/system.md"} +``` + +**Impacto**: Father Bot opera sin su system prompt completo (369 lineas de instrucciones, pipeline, +seguridad) y solo usa la description de una linea del config.yaml. Esto degrada severamente su +comportamiento. + +## Arquitectura + +- `internal/config/schema.go` — MODIFICAR: agregar campo `ConfigDir` a `AgentConfig` +- `internal/config/loader.go` — MODIFICAR: poblar `ConfigDir` con el directorio del config +- `devagents/llm.go` — MODIFICAR: usar `ConfigDir` en vez de hardcodear `agents/` + +No hay cambios en `pkg/` (puro). Los cambios son en el loader (impuro) y runtime (impuro). + +## Tareas + +### Fase 1: Fix + +- [ ] 1.1 Agregar campo `ConfigDir string` (no YAML, solo runtime) a `AgentConfig` +- [ ] 1.2 En `config.Load()`, poblar `ConfigDir` con `filepath.Dir(path)` +- [ ] 1.3 En `devagents/llm.go`, usar `a.cfg.ConfigDir` para resolver `system_prompt_file` + +### Fase 2: Tests + +- [ ] 2.1 Test unitario que verifica que `Load()` puebla `ConfigDir` +- [ ] 2.2 `go build -tags goolm ./...` compila sin errores +- [ ] 2.3 `go test -tags goolm ./...` pasa sin errores + +### Fase 3: Docs + +- [ ] 3.1 Cerrar issue, mover a completed + +## Ejemplo de uso + +Antes (roto): +``` +config en: agents/_specials/father-bot/config.yaml +system_prompt_file: prompts/system.md +resuelve: agents/father-bot/prompts/system.md ← NO EXISTE +resultado: usa description como fallback +``` + +Despues (correcto): +``` +config en: agents/_specials/father-bot/config.yaml +ConfigDir: agents/_specials/father-bot +system_prompt_file: prompts/system.md +resuelve: agents/_specials/father-bot/prompts/system.md ← CORRECTO +``` + +## Decisiones de diseno + +1. **`ConfigDir` como campo runtime**: no se serializa en YAML (`yaml:"-"`), se puebla + automaticamente por el loader. Cero impacto en configs existentes. +2. **Genérico**: el fix funciona para cualquier agente en cualquier ubicacion, no solo _specials. + +## Riesgos + +- Bajo riesgo: cambio minimo y auto-contenido. El campo nuevo es backward-compatible. diff --git a/dev/issues/README.md b/dev/issues/README.md index 86102d9..2f173c4 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -57,3 +57,4 @@ afectados y notas de implementacion. | 44 | Formalizar pipeline de creacion de agentes | [0044-formalize-agent-creation-pipeline.md](completed/0044-formalize-agent-creation-pipeline.md) | completado | | 45 | DM rooms sin E2EE en notify-developer.sh | [0045-notify-encrypted-rooms.md](completed/0045-notify-encrypted-rooms.md) | completado | | 46 | Progreso en tiempo real para Father Bot | [0046-father-bot-progress.md](completed/0046-father-bot-progress.md) | completado | +| 47 | System prompt no se carga para agentes en _specials/ | [0047-fix-system-prompt-path.md](0047-fix-system-prompt-path.md) | pendiente | From 1b499c9b67be29766abc5df0912b9c9289623a83 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 10 Apr 2026 23:21:23 +0000 Subject: [PATCH 2/4] fix: resolver system_prompt_file relativo al directorio del config Antes, el runtime construia la ruta del system prompt como agents//, lo cual fallaba para agentes en agents/_specials/ (como Father Bot). Ahora: 1. config.Load() guarda el directorio del config en ConfigDir 2. llm.go usa ConfigDir para resolver rutas relativas Esto corrige que Father Bot operara sin su system prompt completo (369 lineas de instrucciones, pipeline, seguridad) usando solo la description de una linea como fallback. Co-Authored-By: Claude Opus 4.6 (1M context) --- devagents/llm.go | 4 ++-- internal/config/loader.go | 3 +++ internal/config/schema.go | 5 +++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/devagents/llm.go b/devagents/llm.go index 6cd1762..eb84a96 100644 --- a/devagents/llm.go +++ b/devagents/llm.go @@ -29,8 +29,8 @@ func (a *Agent) runLLM(ctx context.Context, msgCtx decision.MessageContext, memK // Load system prompt from file if configured, else use description systemPrompt := a.cfg.Agent.Description if spFile := a.cfg.LLM.Reasoning.SystemPromptFile; spFile != "" { - // Resolve path relative to agent directory - spPath := filepath.Join("agents", a.cfg.Agent.ID, spFile) + // Resolve path relative to the config directory (handles _specials/ and custom locations) + spPath := filepath.Join(a.cfg.ConfigDir, spFile) if data, err := os.ReadFile(spPath); err == nil { systemPrompt = string(data) } else { diff --git a/internal/config/loader.go b/internal/config/loader.go index 4bf891f..30d5fff 100644 --- a/internal/config/loader.go +++ b/internal/config/loader.go @@ -3,6 +3,7 @@ package config import ( "fmt" "os" + "path/filepath" "gopkg.in/yaml.v3" ) @@ -26,6 +27,8 @@ func Load(path string) (*AgentConfig, error) { return nil, fmt.Errorf("invalid config %s: %w", path, err) } + cfg.ConfigDir = filepath.Dir(path) + return &cfg, nil } diff --git a/internal/config/schema.go b/internal/config/schema.go index 7cb03b4..089d8f5 100644 --- a/internal/config/schema.go +++ b/internal/config/schema.go @@ -16,6 +16,11 @@ type AgentConfig struct { Storage StorageCfg `yaml:"storage"` Memory MemoryCfg `yaml:"memory"` Skills SkillsCfg `yaml:"skills"` + + // ConfigDir is the directory containing the config file. Set by the loader + // at load time, not from YAML. Used to resolve relative paths like + // system_prompt_file correctly regardless of where the agent lives. + ConfigDir string `yaml:"-"` } // ── Identity ────────────────────────────────────────────────────────────── From 06501e2bcc59030ffc6b8206161cc9a54ef64416 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 10 Apr 2026 23:21:28 +0000 Subject: [PATCH 3/4] test: verificar que Load() puebla ConfigDir correctamente Test con directorio anidado (agents/_specials/father-bot/) que confirma que ConfigDir se resuelve al directorio padre del config y que system_prompt_file se puede resolver relativo a el. Co-Authored-By: Claude Opus 4.6 (1M context) --- internal/config/loader_test.go | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/internal/config/loader_test.go b/internal/config/loader_test.go index 432d0fe..b4f837e 100644 --- a/internal/config/loader_test.go +++ b/internal/config/loader_test.go @@ -50,6 +50,47 @@ llm: } } +// ── 2.1b: ConfigDir populated from file path ─────────────────────────── + +func TestLoad_ConfigDir(t *testing.T) { + // Create a nested directory to simulate agents/_specials/father-bot/ + dir := filepath.Join(t.TempDir(), "agents", "_specials", "father-bot") + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatal(err) + } + path := writeYAML(t, dir, "config.yaml", ` +agent: + id: father-bot +matrix: + homeserver: https://matrix.example.com + user_id: "@father-bot:example.com" +llm: + primary: + provider: claude-code + claude_code: + binary: claude + reasoning: + system_prompt_file: prompts/system.md +`) + cfg, err := Load(path) + if err != nil { + t.Fatalf("Load() error: %v", err) + } + if cfg.ConfigDir != dir { + t.Errorf("ConfigDir = %q, want %q", cfg.ConfigDir, dir) + } + // Verify that joining ConfigDir + system_prompt_file gives the right path + spPath := filepath.Join(cfg.ConfigDir, cfg.LLM.Reasoning.SystemPromptFile) + wantSuffix := filepath.Join("agents", "_specials", "father-bot", "prompts", "system.md") + if !filepath.IsAbs(spPath) { + // When running from TempDir, path will be absolute + t.Logf("spPath = %q (expected to end with %q)", spPath, wantSuffix) + } + if cfg.LLM.Reasoning.SystemPromptFile != "prompts/system.md" { + t.Errorf("SystemPromptFile = %q, want %q", cfg.LLM.Reasoning.SystemPromptFile, "prompts/system.md") + } +} + // ── 2.2: Parse full config with all sections ──────────────────────────── func TestLoad_FullConfig(t *testing.T) { From 5047a321d2ddc1ac60d7e7a0b2c0ae8cd198c8f6 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 10 Apr 2026 23:21:41 +0000 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20cerrar=20issue=200047=20=E2=80=94?= =?UTF-8?q?=20system=20prompt=20path=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- dev/issues/README.md | 2 +- dev/issues/{ => completed}/0047-fix-system-prompt-path.md | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename dev/issues/{ => completed}/0047-fix-system-prompt-path.md (100%) diff --git a/dev/issues/README.md b/dev/issues/README.md index 2f173c4..aef3628 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -57,4 +57,4 @@ afectados y notas de implementacion. | 44 | Formalizar pipeline de creacion de agentes | [0044-formalize-agent-creation-pipeline.md](completed/0044-formalize-agent-creation-pipeline.md) | completado | | 45 | DM rooms sin E2EE en notify-developer.sh | [0045-notify-encrypted-rooms.md](completed/0045-notify-encrypted-rooms.md) | completado | | 46 | Progreso en tiempo real para Father Bot | [0046-father-bot-progress.md](completed/0046-father-bot-progress.md) | completado | -| 47 | System prompt no se carga para agentes en _specials/ | [0047-fix-system-prompt-path.md](0047-fix-system-prompt-path.md) | pendiente | +| 47 | System prompt no se carga para agentes en _specials/ | [0047-fix-system-prompt-path.md](completed/0047-fix-system-prompt-path.md) | completado | diff --git a/dev/issues/0047-fix-system-prompt-path.md b/dev/issues/completed/0047-fix-system-prompt-path.md similarity index 100% rename from dev/issues/0047-fix-system-prompt-path.md rename to dev/issues/completed/0047-fix-system-prompt-path.md