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
8 changes: 8 additions & 0 deletions .github/workflows/aw-failure-investigator.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .github/workflows/daily-squid-image-scan.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .github/workflows/dependabot-go-checker.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions .github/workflows/engine-conformance-agy.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .github/workflows/engine-conformance-agy.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ engine:
env:
ENGINE_CONFORMANCE_SENTINEL: conformance-agy
tools:
cli-proxy: false
work-queue:
worker: true
require-assignment: true
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/eslint-monster.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .github/workflows/lint-monster.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .github/workflows/objective-impact-report.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .github/workflows/pr-sous-chef.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .github/workflows/semantic-function-refactor.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion actions/setup/js/safe_output_type_validator.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ const SAMPLE_VALIDATION_CONFIG = {
defaultMax: 1,
fields: {
issue_number: { optionalPositiveInteger: true },
duplicate_of: { issueOrPRNumber: true },
state_reason: { type: "string", enum: ["completed", "not_planned", "duplicate"] },
},
},
dismiss_pull_request_review: {
Expand Down Expand Up @@ -313,6 +315,15 @@ describe("safe_output_type_validator", () => {
});

describe("validateItem", () => {
it("preserves an agent-selected duplicate state reason in API casing", async () => {
const { validateItem } = await import("./safe_output_type_validator.cjs");

const result = validateItem({ type: "close_issue", issue_number: 2, duplicate_of: 1, state_reason: "DUPLICATE" }, "close_issue", 1);

expect(result.isValid).toBe(true);
expect(result.normalizedItem).toEqual({ type: "close_issue", issue_number: 2, duplicate_of: 1, state_reason: "duplicate" });
});

it("preserves explicit JSON null for built-in ledger values", async () => {
const { validateItem, resetValidationConfigCache } = await import("./safe_output_type_validator.cjs");
process.env.GH_AW_VALIDATION_CONFIG = JSON.stringify({
Expand Down Expand Up @@ -1601,7 +1612,6 @@ describe("safe_output_type_validator", () => {
{ itemType: "upload_asset", item: { type: "upload_asset", path: "image.png", targetFileName: "../../.git/config" }, fieldName: "targetFileName" },
{ itemType: "create_issue", item: { type: "create_issue", title: "Test", body: "Detailed issue body text.", assignees: ["octocat"] }, fieldName: "assignees" },
{ itemType: "create_discussion", item: { type: "create_discussion", title: "Test", body: "This discussion body is intentionally long enough for validation.", labels: ["security"] }, fieldName: "labels" },
{ itemType: "close_issue", item: { type: "close_issue", issue_number: 1, state_reason: "not_planned" }, fieldName: "state_reason" },
{ itemType: "push_to_pull_request_branch", item: { type: "push_to_pull_request_branch", message: "Apply changes", diff_size: 0 }, fieldName: "diff_size" },
{ itemType: "create_pull_request", item: { type: "create_pull_request", title: "Fix bug", body: "Fixes the thing", branch: "fix/bug", base_commit: "abc123deadbeef" }, fieldName: "base_commit" },
])("should strip undeclared $itemType.$fieldName", async ({ itemType, item, fieldName }) => {
Expand Down
7 changes: 4 additions & 3 deletions pkg/workflow/agy_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ func TestAgyProductionConformancePermissionsAreBounded(t *testing.T) {
assert.Contains(t, string(lock), `GH_AW_SAFE_OUTPUTS_STAGED: "true"`)
assert.Contains(t, string(lock), `"threat_detection":{"mode":"disabled"}`)
assert.NotContains(t, callee.Jobs, "detection")
assertAgyConformanceProbes(t, callee, entry.id == "engine-conformance-agy")
assertAgyConformanceProbes(t, callee, false)
})
}
}
Expand All @@ -356,6 +356,7 @@ func TestAgyConformanceEntryPointsShareConfiguration(t *testing.T) {
assert.Equal(t, map[string]any{"workflow_dispatch": nil}, canonical["on"])
assert.Equal(t, []any{"shared/agy-conformance.md", "shared/engine-conformance-worker.md"}, canonical["imports"])
assert.Equal(t, map[string]any{
"cli-proxy": false,
"work-queue": map[string]any{"worker": true, "require-assignment": true},
}, canonical["tools"])
canonical["imports"] = []any{"shared/agy-conformance.md"}
Expand Down Expand Up @@ -425,7 +426,7 @@ func TestAgySmokeLabelCommandIsCentrallyRouted(t *testing.T) {
assert.NotContains(t, string(lock), "remove_trigger_label")
}

func assertAgyConformanceProbes(t *testing.T, compiled agyConformanceWorkflow, workQueueWorker bool) {
func assertAgyConformanceProbes(t *testing.T, compiled agyConformanceWorkflow, workQueueCLIServer bool) {
t.Helper()
steps, environment, commands := agyConformanceStepContent(compiled)
execution := steps["Execute experimental Agy CLI"]
Expand Down Expand Up @@ -455,7 +456,7 @@ func assertAgyConformanceProbes(t *testing.T, compiled agyConformanceWorkflow, w
for _, expected := range []string{`"agy-native"`, `"native-challenge"`, "--exclude-env GEMINI_API_KEY"} {
assert.Contains(t, commands, expected, "compiled commands must retain %s", expected)
}
if workQueueWorker {
if workQueueCLIServer {
assert.Contains(t, commands, `export GH_AW_MCP_CLI_SERVERS='["agy-native","mcpscripts","safeoutputs","work-queue"]'`)
} else {
assert.NotContains(t, commands, "export GH_AW_MCP_CLI_SERVERS=")
Expand Down
27 changes: 27 additions & 0 deletions pkg/workflow/safe_output_validation_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,33 @@ func TestValidationConfigCoversToolInputSchemas(t *testing.T) {
}
}

func TestValidationConfigCoversInjectedToolProperties(t *testing.T) {
configs := map[string]*SafeOutputsConfig{
"omitted state-reason": {
CloseIssues: &CloseIssuesConfig{},
},
"list state-reason and allowed-events": {
CloseIssues: &CloseIssuesConfig{AllowedStateReason: []string{"completed", "duplicate"}},
SubmitPullRequestReview: &SubmitPullRequestReviewConfig{
AllowedEvents: []string{"APPROVE", "COMMENT"},
},
},
}

for name, safeOutputs := range configs {
t.Run(name, func(t *testing.T) {
injections := computePropertyInjections(safeOutputs)
for toolName, properties := range injections {
for propertyName := range properties {
if _, ok := ValidationConfig[toolName].Fields[propertyName]; !ok {
t.Errorf("%s injected property %q is missing from ValidationConfig", toolName, propertyName)
}
}
}
})
}
}

func TestCreateDiscussionBodyMinLength(t *testing.T) {
config, ok := ValidationConfig["create_discussion"]
if !ok {
Expand Down
1 change: 1 addition & 0 deletions pkg/workflow/safe_outputs_validation_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ var ValidationConfig = map[string]TypeValidationConfig{
"body": {Type: "string", Sanitize: true, MaxLength: MaxBodyLength},
"issue_number": {OptionalPositiveInteger: true},
"duplicate_of": {IssueOrPRNumber: true},
"state_reason": {Type: "string", Enum: closeIssueStateReasonValues},
"rationale": {Type: "string", Sanitize: true, MaxLength: 280, StripOnError: true},
"confidence": {Type: "string", Enum: []string{"LOW", "MEDIUM", "HIGH"}, StripOnError: true},
"suggest": {Type: "boolean"},
Expand Down
Loading