diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 75850e5771..fd730d3406 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -27,7 +27,7 @@ # Imports: # - shared/mood.md # -# frontmatter-hash: 1f357e423cb4bfab62f37e64c38505cec9d797e4e00d87b1fd982954d90677f9 +# frontmatter-hash: b32c079c9a3f0098fc4c5c9e6776ee58aa3235aaf0b18037a55e81605ed4adf5 # # Effective stop-time: 2026-03-03 16:27:58 @@ -191,7 +191,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"update_issue":{"max":1}} EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'EOF' [ @@ -256,6 +256,70 @@ jobs: }, "name": "add_comment" }, + { + "description": "Update an existing GitHub issue's status, title, labels, assignees, milestone, or body. Body updates support replacing, appending to, prepending content, or updating a per-run \"island\" section. CONSTRAINTS: Maximum 1 issue(s) can be updated.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "assignees": { + "description": "Replace the issue assignees with this list of GitHub usernames (e.g., ['octocat', 'mona']).", + "items": { + "type": "string" + }, + "type": "array" + }, + "body": { + "description": "Issue body content in Markdown. For 'replace', this becomes the entire body. For 'append'/'prepend', this content is added with a separator and an attribution footer. For 'replace-island', only the run-specific section is updated.", + "type": "string" + }, + "issue_number": { + "description": "Issue number to update. This is the numeric ID from the GitHub URL (e.g., 789 in github.com/owner/repo/issues/789). Required when the workflow target is '*' (any issue).", + "type": [ + "number", + "string" + ] + }, + "labels": { + "description": "Replace the issue labels with this list (e.g., ['bug', 'tracking:foo']). Labels must exist in the repository.", + "items": { + "type": "string" + }, + "type": "array" + }, + "milestone": { + "description": "Milestone number to assign (e.g., 1). Use null to clear.", + "type": [ + "number", + "string" + ] + }, + "operation": { + "description": "How to update the issue body: 'append' (default - add to end with separator), 'prepend' (add to start with separator), 'replace' (overwrite entire body), or 'replace-island' (update a run-specific section).", + "enum": [ + "replace", + "append", + "prepend", + "replace-island" + ], + "type": "string" + }, + "status": { + "description": "New issue status: 'open' to reopen a closed issue, 'closed' to close an open issue.", + "enum": [ + "open", + "closed" + ], + "type": "string" + }, + "title": { + "description": "New issue title to replace the existing title.", + "type": "string" + } + }, + "type": "object" + }, + "name": "update_issue" + }, { "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", "inputSchema": { @@ -407,6 +471,32 @@ jobs: "maxLength": 65000 } } + }, + "update_issue": { + "defaultMax": 1, + "fields": { + "body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "issue_number": { + "issueOrPRNumber": true + }, + "status": { + "type": "string", + "enum": [ + "open", + "closed" + ] + }, + "title": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + }, + "customValidation": "requiresOneOf:status,title,body" } } EOF @@ -1178,7 +1268,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_issue\":{\"expires\":24,\"labels\":[\"cookie\"],\"max\":1,\"title_prefix\":\"[CI Failure Doctor] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_issue\":{\"expires\":24,\"labels\":[\"cookie\"],\"max\":1,\"title_prefix\":\"[CI Failure Doctor] \"},\"missing_data\":{},\"missing_tool\":{},\"update_issue\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/ci-doctor.md b/.github/workflows/ci-doctor.md index afd256ace8..240006208d 100644 --- a/.github/workflows/ci-doctor.md +++ b/.github/workflows/ci-doctor.md @@ -32,6 +32,7 @@ safe-outputs: title-prefix: "[CI Failure Doctor] " labels: [cookie] add-comment: + update-issue: noop: messages: footer: "> 🩺 *Diagnosis provided by [{workflow_name}]({run_url})*" @@ -124,18 +125,31 @@ You are the CI Failure Doctor, an expert investigative agent that analyzes faile 2. **Update Pattern Database**: Enhance knowledge with new findings by updating pattern files 3. **Save Artifacts**: Store detailed logs and analysis in the cached directories -### Phase 6: Looking for existing issues - -1. **Convert the report to a search query** - - Use any advanced search features in GitHub Issues to find related issues - - Look for keywords, error messages, and patterns in existing issues -2. **Judge each match issues for relevance** - - Analyze the content of the issues found by the search and judge if they are similar to this issue. -3. **Add issue comment to duplicate issue and finish** - - If you find a duplicate issue, add a comment with your findings and close the investigation. - - Do NOT open a new issue since you found a duplicate already (skip next phases). - -### Phase 6: Reporting and Recommendations +### Phase 6: Looking for existing issues and closing older ones + +1. **Search for existing CI failure doctor issues** + - Use GitHub Issues search to find issues with label "cookie" and title prefix "[CI Failure Doctor]" + - Look for both open and recently closed issues (within the last 7 days) + - Search for keywords, error messages, and patterns from the current failure +2. **Judge each match for relevance** + - Analyze the content of found issues to determine if they are similar to the current failure + - Check if they describe the same root cause, error pattern, or affected components + - Identify truly duplicate issues vs. unrelated failures +3. **Close older duplicate issues** + - If you find older open issues that are duplicates of the current failure: + - Add a comment explaining this is a duplicate of the new investigation + - Use the `update-issue` tool with `state: "closed"` and `state_reason: "not_planned"` to close them + - Include a link to the new issue in the comment + - If older issues describe resolved problems that are recurring: + - Keep them open but add a comment linking to the new occurrence +4. **Handle duplicate detection** + - If you find a very recent duplicate issue (opened within the last hour): + - Add a comment with your findings to the existing issue + - Do NOT open a new issue (skip next phases) + - Exit the workflow + - Otherwise, continue to create a new issue with fresh investigation data + +### Phase 7: Reporting and Recommendations 1. **Create Investigation Report**: Generate a comprehensive analysis including: - **Executive Summary**: Quick overview of the failure - **Root Cause**: Detailed explanation of what went wrong diff --git a/pkg/parser/import_processor.go b/pkg/parser/import_processor.go index f71ee766a9..c73573aab5 100644 --- a/pkg/parser/import_processor.go +++ b/pkg/parser/import_processor.go @@ -169,7 +169,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a var toolsBuilder strings.Builder var mcpServersBuilder strings.Builder var markdownBuilder strings.Builder // Only used for imports WITH inputs (compile-time substitution) - var importPaths []string // NEW: Track import paths for runtime-import macro generation + var importPaths []string // NEW: Track import paths for runtime-import macro generation var stepsBuilder strings.Builder var copilotSetupStepsBuilder strings.Builder // Track copilot-setup-steps.yml separately var runtimesBuilder strings.Builder @@ -317,7 +317,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a } else { // Has inputs - must inline for compile-time substitution log.Printf("Agent file has inputs - will be inlined instead of runtime-imported") - + // For agent files, extract markdown content (only when inputs are present) markdownContent, err := processIncludedFileWithVisited(item.fullPath, item.sectionName, false, visited) if err != nil { @@ -489,7 +489,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a } else { // Has inputs - must inline for compile-time substitution log.Printf("Import %s has inputs - will be inlined for compile-time substitution", importRelPath) - + // Extract markdown content from imported file (only for imports with inputs) markdownContent, err := processIncludedFileWithVisited(item.fullPath, item.sectionName, false, visited) if err != nil { @@ -639,7 +639,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a MergedSafeOutputs: safeOutputs, MergedSafeInputs: safeInputs, MergedMarkdown: markdownBuilder.String(), // Only imports WITH inputs (for compile-time substitution) - ImportPaths: importPaths, // Import paths for runtime-import macro generation + ImportPaths: importPaths, // Import paths for runtime-import macro generation MergedSteps: stepsBuilder.String(), CopilotSetupSteps: copilotSetupStepsBuilder.String(), MergedRuntimes: runtimesBuilder.String(),