diff --git a/pkg/cli/deps_report.go b/pkg/cli/deps_report.go index 9cd650b691b..7e6378ec51f 100644 --- a/pkg/cli/deps_report.go +++ b/pkg/cli/deps_report.go @@ -190,6 +190,8 @@ func DisplayDependencyReport(report *DependencyReport) { // DisplayDependencyReportJSON outputs the dependency report in JSON format func DisplayDependencyReportJSON(report *DependencyReport) error { + depsReportLog.Printf("Generating JSON dependency report: %d total, %d outdated, %d advisories", report.TotalDeps, len(report.Outdated), len(report.Advisories)) + // Calculate percentages outdatedPercentage := 0.0 if report.DirectDeps > 0 { @@ -270,6 +272,8 @@ type DependencyInfoWithIndirect struct { // parseGoModWithIndirect parses go.mod including indirect dependencies func parseGoModWithIndirect(path string) ([]DependencyInfoWithIndirect, error) { + depsReportLog.Printf("Parsing go.mod file: %s", path) + content, err := os.ReadFile(path) if err != nil { return nil, err @@ -313,6 +317,7 @@ func parseGoModWithIndirect(path string) ([]DependencyInfoWithIndirect, error) { } } + depsReportLog.Printf("Parsed go.mod: %d total dependencies", len(deps)) return deps, nil } diff --git a/pkg/parser/import_cycle.go b/pkg/parser/import_cycle.go index 4e455608bf7..21f9a30c2a8 100644 --- a/pkg/parser/import_cycle.go +++ b/pkg/parser/import_cycle.go @@ -8,6 +8,8 @@ import "sort" // findCyclePath uses DFS to find a complete cycle path in the dependency graph. // Returns a path showing the full chain including the back-edge (e.g., ["b.md", "c.md", "d.md", "b.md"]). func findCyclePath(cycleNodes map[string]bool, dependencies map[string][]string) []string { + importLog.Printf("Finding cycle path among %d cycle nodes", len(cycleNodes)) + // Pick any node in the cycle as a starting point (use sorted order for determinism) var startNode string sortedNodes := make([]string, 0, len(cycleNodes)) @@ -18,16 +20,21 @@ func findCyclePath(cycleNodes map[string]bool, dependencies map[string][]string) if len(sortedNodes) > 0 { startNode = sortedNodes[0] } else { + importLog.Print("No cycle nodes found, cannot determine cycle path") return nil } + importLog.Printf("Starting DFS cycle detection from node: %s", startNode) + // Use DFS to find a path from startNode back to itself visited := make(map[string]bool) path := []string{} if dfsForCycle(startNode, startNode, cycleNodes, dependencies, visited, &path, true) { + importLog.Printf("Cycle path found: %v", path) return path } + importLog.Print("DFS completed but no cycle path could be constructed") return nil } @@ -53,6 +60,7 @@ func dfsForCycle(current, target string, cycleNodes map[string]bool, dependencie for _, dep := range sortedDeps { // Found the cycle - we've reached the target again if !isFirst && dep == target { + importLog.Printf("Cycle back-edge found: %s -> %s", current, dep) *path = append(*path, dep) // Add the back-edge return true } diff --git a/pkg/parser/schedule_cron_detection.go b/pkg/parser/schedule_cron_detection.go index 8cc56cbe4f3..396d3af7a08 100644 --- a/pkg/parser/schedule_cron_detection.go +++ b/pkg/parser/schedule_cron_detection.go @@ -37,7 +37,11 @@ func IsDailyCron(cron string) bool { } } - return fields[2] == "*" && fields[3] == "*" && fields[4] == "*" + result := fields[2] == "*" && fields[3] == "*" && fields[4] == "*" + if result { + log.Printf("Cron expression classified as daily: %q (minute=%s, hour=%s)", cron, minute, hour) + } + return result } // IsHourlyCron checks if a cron expression represents an hourly interval with a fixed minute @@ -67,7 +71,11 @@ func IsHourlyCron(cron string) bool { } // Check remaining fields are wildcards - return fields[2] == "*" && fields[3] == "*" && fields[4] == "*" + result := fields[2] == "*" && fields[3] == "*" && fields[4] == "*" + if result { + log.Printf("Cron expression classified as hourly: %q (minute=%s, hour=%s)", cron, minute, hour) + } + return result } // IsWeeklyCron checks if a cron expression represents a weekly schedule at a fixed time @@ -125,6 +133,7 @@ func IsCronExpression(input string) bool { // A cron expression has exactly 5 fields fields := strings.Fields(input) if len(fields) != 5 { + log.Printf("Input is not a cron expression (expected 5 fields, got %d): %q", len(fields), input) return false } diff --git a/pkg/parser/schema_compiler.go b/pkg/parser/schema_compiler.go index 26d20b255a9..55d7486c469 100644 --- a/pkg/parser/schema_compiler.go +++ b/pkg/parser/schema_compiler.go @@ -137,18 +137,23 @@ func GetSafeOutputTypeKeys() ([]string, error) { } func validateWithSchema(frontmatter map[string]any, schemaJSON, context string) error { + schemaCompilerLog.Printf("Validating frontmatter against schema for context: %s (%d fields)", context, len(frontmatter)) + // Determine which cached schema to use based on the schemaJSON var schema *jsonschema.Schema var err error switch schemaJSON { case mainWorkflowSchema: + schemaCompilerLog.Print("Using cached main workflow schema") schema, err = getCompiledMainWorkflowSchema() case mcpConfigSchema: + schemaCompilerLog.Print("Using cached MCP config schema") schema, err = getCompiledMcpConfigSchema() default: // Fallback for unknown schemas (shouldn't happen in normal operation) // Compile the schema on-the-fly + schemaCompilerLog.Print("Compiling unknown schema on-the-fly") schema, err = compileSchema(schemaJSON, "http://contoso.com/schema.json") } @@ -177,9 +182,11 @@ func validateWithSchema(frontmatter map[string]any, schemaJSON, context string) // Validate the normalized frontmatter if err := schema.Validate(normalizedFrontmatter); err != nil { + schemaCompilerLog.Printf("Schema validation failed for %s: %v", context, err) return err } + schemaCompilerLog.Printf("Schema validation passed for context: %s", context) return nil } diff --git a/pkg/workflow/codex_mcp.go b/pkg/workflow/codex_mcp.go index b2e140d9183..0399a549488 100644 --- a/pkg/workflow/codex_mcp.go +++ b/pkg/workflow/codex_mcp.go @@ -164,12 +164,13 @@ func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]an // renderCodexMCPConfigWithContext generates custom MCP server configuration for a single tool in codex workflow config.toml // This version includes workflowData to determine if localhost URLs should be rewritten func (e *CodexEngine) renderCodexMCPConfigWithContext(yaml *strings.Builder, toolName string, toolConfig map[string]any, workflowData *WorkflowData) error { - yaml.WriteString(" \n") - fmt.Fprintf(yaml, " [mcp_servers.%s]\n", toolName) - // Determine if localhost URLs should be rewritten to host.docker.internal // This is needed when firewall is enabled (agent is not disabled) rewriteLocalhost := shouldRewriteLocalhostToDocker(workflowData) + codexMCPLog.Printf("Rendering TOML MCP config for custom tool: %s (rewrite_localhost=%v)", toolName, rewriteLocalhost) + + yaml.WriteString(" \n") + fmt.Fprintf(yaml, " [mcp_servers.%s]\n", toolName) // Use the shared MCP config renderer with TOML format renderer := MCPConfigRenderer{ @@ -180,6 +181,7 @@ func (e *CodexEngine) renderCodexMCPConfigWithContext(yaml *strings.Builder, too err := renderSharedMCPConfig(yaml, toolName, toolConfig, renderer) if err != nil { + codexMCPLog.Printf("Failed to render TOML MCP config for tool %s: %v", toolName, err) return err } @@ -191,6 +193,7 @@ func (e *CodexEngine) renderCodexMCPConfigWithContext(yaml *strings.Builder, too func (e *CodexEngine) renderCodexJSONMCPConfigWithContext(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool, workflowData *WorkflowData) error { // Determine if localhost URLs should be rewritten to host.docker.internal rewriteLocalhost := shouldRewriteLocalhostToDocker(workflowData) + codexMCPLog.Printf("Rendering JSON MCP config for gateway tool: %s (isLast=%v, rewrite_localhost=%v)", toolName, isLast, rewriteLocalhost) // Use the shared renderer with JSON format for gateway renderer := MCPConfigRenderer{ @@ -203,6 +206,7 @@ func (e *CodexEngine) renderCodexJSONMCPConfigWithContext(yaml *strings.Builder, err := renderSharedMCPConfig(yaml, toolName, toolConfig, renderer) if err != nil { + codexMCPLog.Printf("Failed to render JSON MCP config for tool %s: %v", toolName, err) return err }