Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 3 additions & 19 deletions actions/setup/js/safe_output_handler_manager.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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"));

Expand All @@ -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))];
Expand Down
25 changes: 10 additions & 15 deletions actions/setup/js/safe_output_handler_manager.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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" },
];
Expand All @@ -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"));
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/agent-factory-status.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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) | - | - |
Expand Down
14 changes: 8 additions & 6 deletions docs/src/content/docs/reference/frontmatter-full.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Loading