diff --git a/actions/setup/js/safe_output_handler_manager.cjs b/actions/setup/js/safe_output_handler_manager.cjs index 2d3626a7a0..d4fa61d0ea 100644 --- a/actions/setup/js/safe_output_handler_manager.cjs +++ b/actions/setup/js/safe_output_handler_manager.cjs @@ -157,13 +157,7 @@ const HANDLER_MAP = { * Other standalone types: assign_to_agent, create_agent_session, upload_asset, noop * - Have dedicated processing steps with specialized logic */ -const STANDALONE_STEP_TYPES = new Set(["assign_to_agent", "create_agent_session", "create_project", "create_project_status_update", "update_project", "upload_asset", "noop"]); - -/** - * Project-related message types that are handled by the unified handler - * Used to provide more specific skip reasons - */ -const PROJECT_RELATED_TYPES = new Set(["create_project", "create_project_status_update", "update_project"]); +const STANDALONE_STEP_TYPES = new Set(["assign_to_agent", "create_agent_session", "upload_asset", "noop"]); /** * Load configuration for safe outputs @@ -332,18 +326,14 @@ async function processMessages(messageHandlers, messages) { if (!messageHandler) { // Check if this message type is handled by a standalone step if (STANDALONE_STEP_TYPES.has(messageType)) { - // Determine the specific skip reason based on message type - const isProjectRelated = PROJECT_RELATED_TYPES.has(messageType); - const skipReason = isProjectRelated ? "Handled by project handler step" : "Handled by standalone step"; - // Silently skip - this is handled by a dedicated step - core.debug(`Message ${i + 1} (${messageType}) will be ${skipReason.toLowerCase()}`); + core.debug(`Message ${i + 1} (${messageType}) will be handled by standalone step`); results.push({ type: messageType, messageIndex: i, success: false, skipped: true, - reason: skipReason, + reason: "Handled by standalone step", }); continue; } @@ -866,7 +856,6 @@ async function main() { const successCount = processingResult.results.filter(r => r.success).length; const failureCount = processingResult.results.filter(r => !r.success && !r.deferred && !r.skipped).length; const deferredCount = processingResult.results.filter(r => r.deferred).length; - const skippedProjectResults = processingResult.results.filter(r => r.skipped && r.reason === "Handled by project handler step"); const skippedStandaloneResults = processingResult.results.filter(r => r.skipped && r.reason === "Handled by standalone step"); const skippedNoHandlerResults = processingResult.results.filter(r => !r.success && !r.skipped && r.error?.includes("No handler loaded")); @@ -877,11 +866,6 @@ async function main() { if (deferredCount > 0) { core.info(`Deferred: ${deferredCount}`); } - if (skippedProjectResults.length > 0) { - core.info(`Skipped (project handler step): ${skippedProjectResults.length}`); - const projectTypes = [...new Set(skippedProjectResults.map(r => r.type))]; - core.info(` Types: ${projectTypes.join(", ")}`); - } if (skippedStandaloneResults.length > 0) { core.info(`Skipped (standalone step): ${skippedStandaloneResults.length}`); const standaloneTypes = [...new Set(skippedStandaloneResults.map(r => r.type))]; diff --git a/actions/setup/js/safe_output_handler_manager.test.cjs b/actions/setup/js/safe_output_handler_manager.test.cjs index e93f721689..ea26caf1c9 100644 --- a/actions/setup/js/safe_output_handler_manager.test.cjs +++ b/actions/setup/js/safe_output_handler_manager.test.cjs @@ -395,14 +395,14 @@ describe("Safe Output Handler Manager", () => { it("should silently skip message types handled by standalone steps", async () => { const messages = [ { type: "create_issue", title: "Issue" }, - { type: "update_project", project: "https://github.com/orgs/myorg/projects/42" }, { type: "create_agent_session", title: "Task" }, + { type: "upload_asset", path: "file.txt" }, ]; const mockHandler = vi.fn().mockResolvedValue({ success: true }); // Only create_issue handler is available - // update_project and create_agent_session are handled by standalone steps + // create_agent_session and upload_asset are handled by standalone steps const handlers = new Map([["create_issue", mockHandler]]); const result = await processMessages(handlers, messages); @@ -414,32 +414,32 @@ describe("Safe Output Handler Manager", () => { expect(result.results[0].success).toBe(true); expect(result.results[0].type).toBe("create_issue"); - // Second message should be skipped (project handler step) + // Second message should be skipped (standalone step) expect(result.results[1].success).toBe(false); - expect(result.results[1].type).toBe("update_project"); + expect(result.results[1].type).toBe("create_agent_session"); expect(result.results[1].skipped).toBe(true); - expect(result.results[1].reason).toBe("Handled by project handler step"); + expect(result.results[1].reason).toBe("Handled by standalone step"); // Third message should also be skipped (standalone step) expect(result.results[2].success).toBe(false); - expect(result.results[2].type).toBe("create_agent_session"); + expect(result.results[2].type).toBe("upload_asset"); expect(result.results[2].skipped).toBe(true); expect(result.results[2].reason).toBe("Handled by standalone step"); // Should NOT have logged warnings for standalone step types - expect(core.warning).not.toHaveBeenCalledWith(expect.stringContaining("No handler loaded for message type 'update_project'")); expect(core.warning).not.toHaveBeenCalledWith(expect.stringContaining("No handler loaded for message type 'create_agent_session'")); + expect(core.warning).not.toHaveBeenCalledWith(expect.stringContaining("No handler loaded for message type 'upload_asset'")); // Should have logged debug messages - expect(core.debug).toHaveBeenCalledWith(expect.stringContaining("update_project")); expect(core.debug).toHaveBeenCalledWith(expect.stringContaining("create_agent_session")); + expect(core.debug).toHaveBeenCalledWith(expect.stringContaining("upload_asset")); }); it("should track skipped message types for logging", async () => { const messages = [ { type: "create_issue", title: "Issue" }, - { type: "update_project", project: "https://github.com/orgs/myorg/projects/42" }, { type: "create_agent_session", title: "Task" }, + { type: "upload_asset", path: "file.txt" }, { type: "unknown_type", data: "test" }, { type: "another_unknown", data: "test2" }, ]; @@ -453,15 +453,10 @@ describe("Safe Output Handler Manager", () => { expect(result.success).toBe(true); - // Collect skipped project handler types - const skippedProjectResults = result.results.filter(r => r.skipped && r.reason === "Handled by project handler step"); - const projectTypes = [...new Set(skippedProjectResults.map(r => r.type))]; - expect(projectTypes).toEqual(expect.arrayContaining(["update_project"])); - // Collect skipped standalone types const skippedStandaloneResults = result.results.filter(r => r.skipped && r.reason === "Handled by standalone step"); const standaloneTypes = [...new Set(skippedStandaloneResults.map(r => r.type))]; - expect(standaloneTypes).toEqual(expect.arrayContaining(["create_agent_session"])); + expect(standaloneTypes).toEqual(expect.arrayContaining(["create_agent_session", "upload_asset"])); // Collect skipped no-handler types const skippedNoHandlerResults = result.results.filter(r => !r.success && !r.skipped && r.error?.includes("No handler loaded")); diff --git a/docs/src/content/docs/agent-factory-status.mdx b/docs/src/content/docs/agent-factory-status.mdx index 2bc15014f1..2337b43950 100644 --- a/docs/src/content/docs/agent-factory-status.mdx +++ b/docs/src/content/docs/agent-factory-status.mdx @@ -142,7 +142,7 @@ These are experimental agentic workflows used by the GitHub Next team to learn, | [Terminal Stylist](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/terminal-stylist.md) | copilot | [![Terminal Stylist](https://github.com/githubnext/gh-aw/actions/workflows/terminal-stylist.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/terminal-stylist.lock.yml) | - | - | | [Test Create PR Error Handling](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/test-create-pr-error-handling.md) | claude | [![Test Create PR Error Handling](https://github.com/githubnext/gh-aw/actions/workflows/test-create-pr-error-handling.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/test-create-pr-error-handling.lock.yml) | - | - | | [Test Dispatcher Workflow](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/test-dispatcher.md) | copilot | [![Test Dispatcher Workflow](https://github.com/githubnext/gh-aw/actions/workflows/test-dispatcher.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/test-dispatcher.lock.yml) | - | - | -| [Test Project URL Default](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/test-project-url-default.md) | copilot | [![Test Project URL Default](https://github.com/githubnext/gh-aw/actions/workflows/test-project-url-default.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/test-project-url-default.lock.yml) | - | - | +| [Test Project URL Explicit Requirement](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/test-project-url-default.md) | copilot | [![Test Project URL Explicit Requirement](https://github.com/githubnext/gh-aw/actions/workflows/test-project-url-default.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/test-project-url-default.lock.yml) | - | - | | [Test YAML Import](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/test-yaml-import.md) | copilot | [![Test YAML Import](https://github.com/githubnext/gh-aw/actions/workflows/test-yaml-import.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/test-yaml-import.lock.yml) | - | - | | [The Daily Repository Chronicle](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-repo-chronicle.md) | copilot | [![The Daily Repository Chronicle](https://github.com/githubnext/gh-aw/actions/workflows/daily-repo-chronicle.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-repo-chronicle.lock.yml) | `0 16 * * 1-5` | - | | [The Great Escapi](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/firewall-escape.md) | copilot | [![The Great Escapi](https://github.com/githubnext/gh-aw/actions/workflows/firewall-escape.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/firewall-escape.lock.yml) | - | - | diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index 4f7882931e..5dbbf2bfd1 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -1950,9 +1950,10 @@ safe-outputs: # (optional) github-token: "${{ secrets.GITHUB_TOKEN }}" - # Target project URL for update-project operations. Agent messages can optionally - # override this by providing an explicit project field in the output. Must be a - # valid GitHub Projects v2 URL. + # Target project URL for update-project operations. This is required in the + # configuration for documentation purposes. Agent messages MUST explicitly include + # the project field in their output - the configured value is not used as a + # fallback. Must be a valid GitHub Projects v2 URL. project: "example-value" # Optional array of project views to create. Each view must have a name and @@ -2106,9 +2107,10 @@ safe-outputs: # (optional) github-token: "${{ secrets.GITHUB_TOKEN }}" - # Target project URL for status update operations. Agent messages can optionally - # override this by providing an explicit project field in the output. Must be a - # valid GitHub Projects v2 URL. + # Target project URL for status update operations. This is required in the + # configuration for documentation purposes. Agent messages MUST explicitly include + # the project field in their output - the configured value is not used as a + # fallback. Must be a valid GitHub Projects v2 URL. project: "example-value" # Option 2: Enable project status updates with default configuration (max=1)