@@ -36,8 +36,8 @@ func TestBuildOrchestrator_BasicShape(t *testing.T) {
3636 t .Fatalf ("expected On section with workflow_dispatch trigger, got %q" , data .On )
3737 }
3838
39- if ! strings .Contains (data .On , "schedule:" ) || ! strings .Contains (data .On , "0 18 * * *" ) {
40- t .Fatalf ("expected On section with daily schedule cron, got %q" , data .On )
39+ if ! strings .Contains (data .On , "schedule:" ) || ! strings .Contains (data .On , "0 * * * *" ) {
40+ t .Fatalf ("expected On section with hourly schedule cron, got %q" , data .On )
4141 }
4242
4343 if strings .TrimSpace (data .Concurrency ) == "" || ! strings .Contains (data .Concurrency , "concurrency:" ) {
@@ -137,7 +137,7 @@ func TestBuildOrchestrator_DispatchOnlyPolicy(t *testing.T) {
137137 spec := & CampaignSpec {
138138 ID : "dispatch-only-campaign" ,
139139 Name : "Dispatch Only Campaign" ,
140- Description : "Campaign orchestrator restricted to dispatch-workflow " ,
140+ Description : "Campaign orchestrator with dispatch and project capabilities " ,
141141 ProjectURL : "https://github.com/orgs/test/projects/1" ,
142142 Workflows : []string {"worker-a" , "worker-b" },
143143 MemoryPaths : []string {"memory/campaigns/dispatch-only-campaign/**" },
@@ -158,14 +158,24 @@ func TestBuildOrchestrator_DispatchOnlyPolicy(t *testing.T) {
158158 if len (data .SafeOutputs .DispatchWorkflow .Workflows ) != 2 {
159159 t .Fatalf ("expected 2 allowlisted workflows, got %d" , len (data .SafeOutputs .DispatchWorkflow .Workflows ))
160160 }
161- if data .SafeOutputs .CreateIssues != nil || data .SafeOutputs .AddComments != nil || data .SafeOutputs .UpdateProjects != nil || data .SafeOutputs .CreateProjectStatusUpdates != nil {
162- t .Fatalf ("expected dispatch-only orchestrator to omit non-dispatch safe outputs" )
161+
162+ // Orchestrators should have update-project and create-project-status-update for dashboard maintenance
163+ if data .SafeOutputs .UpdateProjects == nil {
164+ t .Fatalf ("expected update-project safe output to be enabled" )
165+ }
166+ if data .SafeOutputs .CreateProjectStatusUpdates == nil {
167+ t .Fatalf ("expected create-project-status-update safe output to be enabled" )
168+ }
169+
170+ // Orchestrators should NOT have create-issue or add-comment (workers handle those)
171+ if data .SafeOutputs .CreateIssues != nil || data .SafeOutputs .AddComments != nil {
172+ t .Fatalf ("expected orchestrator to omit create-issue and add-comment safe outputs" )
163173 }
164174
165- // Dispatch-only policy should not grant GitHub tool access to the agent.
175+ // Orchestrators should not have GitHub tool access to the agent.
166176 if data .Tools != nil {
167177 if _ , ok := data .Tools ["github" ]; ok {
168- t .Fatalf ("expected dispatch-only orchestrator to omit github tools" )
178+ t .Fatalf ("expected orchestrator to omit github tools" )
169179 }
170180 }
171181 })
@@ -253,8 +263,26 @@ func TestBuildOrchestrator_GovernanceDoesNotGrantWriteSafeOutputs(t *testing.T)
253263 if data .SafeOutputs .DispatchWorkflow .Max != 3 {
254264 t .Fatalf ("unexpected dispatch-workflow max: got %d, want %d" , data .SafeOutputs .DispatchWorkflow .Max , 3 )
255265 }
256- if data .SafeOutputs .CreateIssues != nil || data .SafeOutputs .AddComments != nil || data .SafeOutputs .UpdateProjects != nil || data .SafeOutputs .CreateProjectStatusUpdates != nil {
257- t .Fatalf ("expected orchestrator to omit non-dispatch safe outputs regardless of governance" )
266+
267+ // Governance should control update-project max
268+ if data .SafeOutputs .UpdateProjects == nil {
269+ t .Fatalf ("expected update-project safe output to be enabled" )
270+ }
271+ if data .SafeOutputs .UpdateProjects .Max != 4 {
272+ t .Fatalf ("unexpected update-project max: got %d, want %d" , data .SafeOutputs .UpdateProjects .Max , 4 )
273+ }
274+
275+ // create-project-status-update should always be enabled
276+ if data .SafeOutputs .CreateProjectStatusUpdates == nil {
277+ t .Fatalf ("expected create-project-status-update safe output to be enabled" )
278+ }
279+ if data .SafeOutputs .CreateProjectStatusUpdates .Max != 1 {
280+ t .Fatalf ("unexpected create-project-status-update max: got %d, want %d" , data .SafeOutputs .CreateProjectStatusUpdates .Max , 1 )
281+ }
282+
283+ // Orchestrators should NOT have create-issue or add-comment (governance MaxCommentsPerRun doesn't grant add-comment)
284+ if data .SafeOutputs .CreateIssues != nil || data .SafeOutputs .AddComments != nil {
285+ t .Fatalf ("expected orchestrator to omit create-issue and add-comment safe outputs regardless of governance" )
258286 }
259287 })
260288}
0 commit comments