diff --git a/pkg/cli/logs_usage_activity.go b/pkg/cli/logs_usage_activity.go index 7c61ed24269..c4d171d0b19 100644 --- a/pkg/cli/logs_usage_activity.go +++ b/pkg/cli/logs_usage_activity.go @@ -7,9 +7,12 @@ import ( "os" "path/filepath" + "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/sliceutil" ) +var logsUsageActivityLog = logger.New("cli:logs_usage_activity") + const usageActivitySummarySchema = "usage-activity-summary/v1" type usageActivitySummary struct { @@ -69,13 +72,16 @@ func loadUsageActivitySummary(runDir string) (*usageActivitySummary, error) { } var summary usageActivitySummary if err := json.Unmarshal(raw, &summary); err != nil { + logsUsageActivityLog.Printf("loadUsageActivitySummary: failed to parse %s, trying next candidate", cleanPath) lastErr = fmt.Errorf("parse usage activity summary %s: %w", cleanPath, err) continue } if summary.Schema != usageActivitySummarySchema { + logsUsageActivityLog.Printf("loadUsageActivitySummary: unsupported schema %q in %s (expected %q)", summary.Schema, cleanPath, usageActivitySummarySchema) lastErr = fmt.Errorf("unsupported usage activity summary schema %q in %s (expected %q)", summary.Schema, cleanPath, usageActivitySummarySchema) continue } + logsUsageActivityLog.Printf("loadUsageActivitySummary: loaded summary from %s", cleanPath) return &summary, nil } return nil, lastErr @@ -93,6 +99,7 @@ func applyUsageActivitySummaryToResult(summary *usageActivitySummary, result *Do } if summary.Firewall != nil && result.FirewallAnalysis == nil { + logsUsageActivityLog.Printf("applyUsageActivitySummaryToResult: backfilling firewall analysis (total=%d allowed=%d blocked=%d)", summary.Firewall.TotalRequests, summary.Firewall.AllowedRequests, summary.Firewall.BlockedRequests) requestsByDomain := maps.Clone(summary.Firewall.RequestsByDomain) if requestsByDomain == nil { requestsByDomain = map[string]DomainRequestStats{} @@ -129,6 +136,7 @@ func applyUsageActivitySummaryToResult(summary *usageActivitySummary, result *Do } if summary.Gateway != nil && result.MCPToolUsage == nil { + logsUsageActivityLog.Printf("applyUsageActivitySummaryToResult: backfilling MCP tool usage from %d gateway server(s)", len(summary.Gateway.Servers)) servers := make([]MCPServerStats, 0, len(summary.Gateway.Servers)) for _, server := range summary.Gateway.Servers { servers = append(servers, MCPServerStats{ diff --git a/pkg/importinpututil/import_input.go b/pkg/importinpututil/import_input.go index 7f4f0a16dda..ceac05694ff 100644 --- a/pkg/importinpututil/import_input.go +++ b/pkg/importinpututil/import_input.go @@ -6,25 +6,33 @@ import ( "reflect" "sort" "strings" + + "github.com/github/gh-aw/pkg/logger" ) +var importInputLog = logger.New("importinpututil:import_input") + // ResolvePathValue resolves either a top-level input key ("count") or a one-level // dotted object sub-key ("config.apiKey") from import inputs. func ResolvePathValue(inputs map[string]any, inputPath string) (any, bool) { top, sub, hasDot := strings.Cut(inputPath, ".") if !hasDot { value, ok := inputs[top] + importInputLog.Printf("ResolvePathValue: top-level key %q found=%t", top, ok) return value, ok } topVal, topOK := inputs[top] if !topOK { + importInputLog.Printf("ResolvePathValue: parent key %q not found for path %q", top, inputPath) return nil, false } obj, isMap := topVal.(map[string]any) if !isMap { + importInputLog.Printf("ResolvePathValue: parent key %q is not an object for path %q", top, inputPath) return nil, false } value, ok := obj[sub] + importInputLog.Printf("ResolvePathValue: sub-key %q under %q found=%t", sub, top, ok) return value, ok } @@ -38,6 +46,7 @@ func FormatResolvedValue(value any) (string, bool) { case map[string]any: return marshalValue(v) case nil: + importInputLog.Print("FormatResolvedValue: nil value, no substitution") return "", false default: return formatReflectiveValue(v) diff --git a/pkg/intent/resolver.go b/pkg/intent/resolver.go index 53005a5bccc..dca9df429fc 100644 --- a/pkg/intent/resolver.go +++ b/pkg/intent/resolver.go @@ -1,5 +1,9 @@ package intent +import "github.com/github/gh-aw/pkg/logger" + +var resolverLog = logger.New("intent:resolver") + type AttributionStatus string const ( @@ -60,29 +64,38 @@ type Resolver struct { } func (r Resolver) ResolvePullRequest(pr PullRequestData) IntentRecord { + resolverLog.Printf("ResolvePullRequest: nodeID=%s explicitIntent=%t closingIssues=%d labels=%d", pr.NodeID, pr.ExplicitIntent != nil, len(pr.ClosingIssues), len(pr.Labels)) + if pr.ExplicitIntent != nil { intent := *pr.ExplicitIntent if intent.ResolverVersion == "" { intent.ResolverVersion = r.ResolverVersion } + resolverLog.Printf("ResolvePullRequest: using explicit metadata for %s", pr.NodeID) return intent } switch len(pr.ClosingIssues) { case 1: + resolverLog.Printf("ResolvePullRequest: attributing %s to single closing issue", pr.NodeID) return r.fromRoot(pr.ClosingIssues[0], SourceClosingIssue, "single_closing_issue") case 0: if len(pr.Labels) > 0 { + resolverLog.Printf("ResolvePullRequest: no closing issue, falling back to %d PR label(s) for %s", len(pr.Labels), pr.NodeID) return r.fromLabels(pr.NodeID, pr.URL, pr.Labels, SourceArtifactLabels, "pull_request_label_fallback") } + resolverLog.Printf("ResolvePullRequest: no intent source for %s, marking unlinked", pr.NodeID) return r.unlinked("no_supported_intent_source") default: + resolverLog.Printf("ResolvePullRequest: %d closing issues for %s, marking ambiguous", len(pr.ClosingIssues), pr.NodeID) return r.ambiguous(SourceClosingIssue, "multiple_closing_issues") } } func (r Resolver) ResolveIssue(nodeID, url string, labels []string) IntentRecord { + resolverLog.Printf("ResolveIssue: nodeID=%s labels=%d", nodeID, len(labels)) if len(labels) == 0 { + resolverLog.Printf("ResolveIssue: no labels for %s, marking unlinked", nodeID) return r.unlinked("no_supported_intent_source") } return IntentRecord{ diff --git a/pkg/workflow/lsp_validation.go b/pkg/workflow/lsp_validation.go index 80f023f9b39..3c84ee15b8a 100644 --- a/pkg/workflow/lsp_validation.go +++ b/pkg/workflow/lsp_validation.go @@ -1,12 +1,20 @@ package workflow -import "fmt" +import ( + "fmt" + + "github.com/github/gh-aw/pkg/logger" +) + +var lspValidationLog = logger.New("workflow:lsp_validation") func (c *Compiler) validateLSPSupport(workflowData *WorkflowData) error { if workflowData == nil || len(workflowData.LSP) == 0 { return nil } + lspValidationLog.Printf("validateLSPSupport: validating %d LSP config(s)", len(workflowData.LSP)) + manager := NewLSPManager(workflowData.LSP) if err := manager.Validate(); err != nil { return err @@ -18,8 +26,10 @@ func (c *Compiler) validateLSPSupport(workflowData *WorkflowData) error { } if engineID != "copilot" { + lspValidationLog.Printf("validateLSPSupport: rejecting unsupported engine %q for LSP", engineID) return fmt.Errorf("lsp is currently only supported for engine: copilot (found engine: %s)", engineID) } + lspValidationLog.Printf("validateLSPSupport: LSP validation passed for engine %q", engineID) return nil } diff --git a/pkg/workflow/skills_frontmatter.go b/pkg/workflow/skills_frontmatter.go index 2f4ca313fcd..7a6b5e86e38 100644 --- a/pkg/workflow/skills_frontmatter.go +++ b/pkg/workflow/skills_frontmatter.go @@ -5,8 +5,12 @@ import ( "fmt" "regexp" "strings" + + "github.com/github/gh-aw/pkg/logger" ) +var skillsFrontmatterLog = logger.New("workflow:skills_frontmatter") + var skillSpecRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@[0-9a-f]{40}$`) var skillSpecExpressionRefRegexp = regexp.MustCompile(`^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)?@\$\{\{.+\}\}$`) var githubActionsExpressionRegexp = regexp.MustCompile(`^\$\{\{.+\}\}$`) @@ -48,6 +52,8 @@ func validateFrontmatterSkills(frontmatter map[string]any) error { return errors.New("skills must be an array of skill references") } + skillsFrontmatterLog.Printf("validateFrontmatterSkills: validating %d skill entr(ies)", len(skills)) + for i, rawSkill := range skills { switch typed := rawSkill.(type) { case string: @@ -124,6 +130,7 @@ func isRepositorySkillSpec(skillSpec string) bool { } func parseRawSkillReferences(rawSkills []any) []SkillReference { + skillsFrontmatterLog.Printf("parseRawSkillReferences: parsing %d raw skill entr(ies)", len(rawSkills)) refs := make([]SkillReference, 0, len(rawSkills)) for _, rawSkill := range rawSkills { switch typed := rawSkill.(type) { @@ -151,5 +158,6 @@ func parseRawSkillReferences(rawSkills []any) []SkillReference { if len(refs) == 0 { return nil } + skillsFrontmatterLog.Printf("parseRawSkillReferences: parsed %d skill reference(s)", len(refs)) return refs }