Skip to content

Commit 128ee74

Browse files
Copilotpelikhan
andauthored
Cover partial-claim gating and single-item claim arrays
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
1 parent 1e87165 commit 128ee74

5 files changed

Lines changed: 18 additions & 5 deletions

File tree

‎actions/setup/js/create_prompt.cjs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,9 @@ function renderWorkClaim(payload) {
168168
const claim = JSON.stringify({ id: item.claim_id, work_id: item.work_id }).replace(/</g, "\\u003c");
169169
return `${work}\n${claim}`;
170170
});
171-
const instructions =
172-
assignments.length === 1
173-
? 'When the work is complete, call work_queue_claim_finish with outcome "completed" to record the finish intent. If you cannot complete the work, call it with outcome "cancelled".'
174-
: 'Call work_queue_claim_finish with claim_id and outcome "completed" or "cancelled" for every claim. Tag each safe output with claim_id to apply it only when that claim completes. Untagged outputs require every claim to complete.';
171+
const instructions = !Array.isArray(assignment)
172+
? 'When the work is complete, call work_queue_claim_finish with outcome "completed" to record the finish intent. If you cannot complete the work, call it with outcome "cancelled".'
173+
: 'Call work_queue_claim_finish with claim_id and outcome "completed" or "cancelled" for every claim. Tag each safe output with claim_id to apply it only when that claim completes. Untagged outputs require every claim to complete.';
175174
return `<work-claim>\n${claims.join("\n")}\n${instructions} The claim is only complete after trusted reconciliation verifies it; recording intent alone does not authorize safe outputs.\n</work-claim>\n`;
176175
}
177176

‎actions/setup/js/finish_work_queue_claim.cjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function validWorker(worker) {
2929
return worker && typeof worker === "object" && typeof worker.work_id === "string" && worker.work_id.length > 0 && typeof worker.claim_id === "string" && worker.claim_id.length > 0;
3030
}
3131

32+
/** @param {Array<{claim_id: string, work_id: string}> | null} [workers] */
3233
function readFinishIntent(finishIntentPath = process.env.GH_AW_WORK_QUEUE_FINISH_INTENT || FINISH_INTENT_PATH, workers = null) {
3334
if (!fs.existsSync(finishIntentPath)) return null;
3435
const outcomes = new Set();
@@ -70,6 +71,7 @@ function renderSummary(status) {
7071
const labels = {
7172
unassigned: "No worker claim was assigned; safe outputs may proceed.",
7273
completed: "The effective worker claim was durably completed and verified; safe outputs may proceed.",
74+
partial: "Some claims were completed and verified; only safe outputs tagged with those claims may proceed.",
7375
cancelled: "The worker claim was cancelled; ordinary safe outputs were skipped.",
7476
superseded: "The worker claim is no longer effective; ordinary safe outputs were skipped.",
7577
terminal: "The work is already terminal; ordinary safe outputs were skipped.",

‎actions/setup/js/work_queue_mcp_server.cjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ function createWorkQueueStateTool(snapshot) {
136136
function createWorkQueueFinishTool(options = {}) {
137137
const outputPath = options.finishIntentPath || process.env.GH_AW_WORK_QUEUE_FINISH_INTENT || DEFAULT_FINISH_INTENT_PATH;
138138
const workers = options.worker == null ? [] : Array.isArray(options.worker) ? options.worker : [options.worker];
139-
const multiple = workers.length > 1;
139+
const multiple = Array.isArray(options.worker);
140140
return {
141141
name: "work_queue_claim_finish",
142142
description: multiple ? "Record a finish intent for a trusted inbound claim. Call once for each claim_id." : "Record the finish intent for the trusted inbound work queue claim.",

‎actions/setup/js/work_queue_mcp_server.test.cjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ describe("work queue MCP snapshot", () => {
181181
tool.handler({ claim_id: "a", outcome: "completed" });
182182
tool.handler({ claim_id: "b", outcome: "cancelled" });
183183
expect(fs.readFileSync(finishPath, "utf8")).toBe('{"claim_id":"a","outcome":"completed"}\n{"claim_id":"b","outcome":"cancelled"}\n');
184+
const arrayOfOne = createWorkQueueFinishTool({ finishIntentPath: finishPath, worker: [{ claim_id: "a", work_id: "one" }] });
185+
expect(arrayOfOne.inputSchema.required).toEqual(["claim_id"]);
184186
});
185187

186188
it("returns queue state and finish confirmation through the stdio MCP transport", async () => {

‎pkg/workflow/compiler_safe_outputs_job_test.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,3 +1750,13 @@ func TestBuildSafeOutputItemsManifestUploadStep(t *testing.T) {
17501750
assert.NotContains(t, content, "process-safe-outputs.stdout.log")
17511751
assert.NotContains(t, content, "process-safe-outputs.stderr.log")
17521752
}
1753+
1754+
func TestGateSafeOutputStepsForPartialWorkQueueClaims(t *testing.T) {
1755+
steps := gateSafeOutputSteps([]string{
1756+
" - name: Process safe outputs\n id: process_safe_outputs\n run: echo process\n",
1757+
" - name: Custom action\n id: action_notify\n if: success()\n run: echo notify\n",
1758+
})
1759+
result := strings.Join(steps, "")
1760+
assert.Contains(t, result, "if: "+combineGitHubIfExpressions(workQueueClaimAuthorizedExpression, "")+"\n id: process_safe_outputs")
1761+
assert.Contains(t, result, "id: action_notify\n if: "+combineGitHubIfExpressions("steps.work_queue_claim_reconciliation.outputs.all_authorized == 'true'", "success()"))
1762+
}

0 commit comments

Comments
 (0)