-
Notifications
You must be signed in to change notification settings - Fork 505
Address SPDD safe-output spec gaps #55441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fedbcd0
4d2e6b8
2b937fa
6b88f1c
facce81
f1f3049
1e67eac
a47dd6e
7f78a30
e3acd8e
a4778c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -457,6 +457,10 @@ In the Octokit client this is `githubClient.rest.issues.setLabels(params)`. | |
|
|
||
| **RL-046**: When the `setLabels` REST call fails (e.g., HTTP 422 for an invalid label name), the implementation MUST log a `core.error()` entry and MUST return `{ success: false, error: <message> }`. For HTTP-level failures the call is all-or-nothing — either all label changes are applied or none are. For HTTP 200 responses, see §7.4 for partial-success response handling. | ||
|
|
||
| **RL-046a**: If `setLabels` returns HTTP 404, the implementation MUST treat the request as rejected because the authoritative labeling target or repository is no longer reachable. | ||
|
|
||
| **RL-046b**: If `setLabels` returns HTTP 5xx, times out, or fails with a transport error, the implementation MUST treat the condition as transient and retry according to the retry policy in §7.3. If retries are exhausted, the implementation MUST return `{ success: false, error: <message> }` without reporting a successful replacement. | ||
|
|
||
| ### 7.3 Rate-Limit Retry Policy | ||
|
|
||
| **RL-048**: The `setLabels` REST call (Stage 8) MUST apply the `RATE_LIMIT_RETRY_CONFIG` retry policy from `actions/setup/js/error_recovery.cjs`. This policy covers secondary rate-limit responses (HTTP 403 with Retry-After header) and primary rate-limit responses (HTTP 429). | ||
|
|
@@ -486,6 +490,8 @@ Label allowlists and blocklists are the primary mechanism preventing AI agents f | |
|
|
||
| **RL-049**: Allowlist and blocklist evaluation MUST be performed server-side (in the JavaScript handler executing within GitHub Actions), not by the AI agent. Agents MUST NOT be trusted to self-enforce label restrictions. | ||
|
|
||
| **RL-049a**: A conforming implementation MUST re-check `label_to_add` against the current server-side allowlist and blocklist state immediately before invoking `PUT /repos/{owner}/{repo}/issues/{issue_number}/labels`. If the label is blocked or no longer allowed at that point, the implementation MUST reject the message without calling the write API. | ||
|
|
||
| ### 8.2 Cross-Repository Restrictions | ||
|
|
||
| By default, `replace-label` operates on the repository of the triggering workflow. Cross-repository operation is opt-in and must be explicitly declared. | ||
|
|
@@ -518,6 +524,12 @@ Staged mode provides a mechanism for operators to audit AI agent label-transitio | |
|
|
||
| **RL-056**: When `staged: true`, the implementation MUST NOT call any write API endpoint. Read-only API calls performed during Stage 5 gate checks MAY proceed in staged mode. | ||
|
|
||
| The staged-mode preview SHOULD identify the `label_to_add` value that would be revalidated before the write call so operators can compare previewed transitions against protected-label policy before disabling staged mode. | ||
|
|
||
| ### 8.7 Sync Notes | ||
|
|
||
| The REST failure and retry semantics in [Section 7](#7-error-handling) are mirrored by the `replace_label` outcome-evaluation rules in [`safe-output-outcome-evaluation.md` Section 30](safe-output-outcome-evaluation.md#30-replace_label). Changes to `404`, `5xx`, or `429` handling in either document SHOULD be reviewed against the other document in the same change. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 6b88f1c: |
||
|
|
||
| --- | ||
|
|
||
| ## 9. Compliance Testing | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -683,8 +683,9 @@ No outcome to evaluate. Skip. | |
| | `label_to_add` is present on the item AND `label_to_remove` is absent | `accepted` | | ||
| | `label_to_add` is absent from the item | `rejected` | | ||
| | `label_to_add` is present but `label_to_remove` is also still present | `rejected` (partial failure — remove did not apply) | | ||
| | Item not found (`404`) | `rejected` | | ||
| | API transient failure (`5xx`, timeout, transport error) | `pending` | | ||
| | Item not found (`404`) | Outcome evaluation workers **MUST** classify as `rejected` | | ||
| | API transient failure (`5xx`, timeout, transport error) | Outcome evaluation workers **MUST** classify as `pending` | | ||
| | Rate-limit response (`403` exhaustion or `429`) | Outcome evaluation workers **MUST** classify as `pending` and **SHOULD** reschedule using the reset window | | ||
| | `lifecycle` | N/A — `replace_label` has no lifecycle bot-close behavior | | ||
| | `lifecycle_close` | N/A — `replace_label` has no lifecycle bot-close behavior | | ||
| | `ignored` | N/A — label state is always evaluable when the item is accessible; no time-bounded engagement signal applies | | ||
|
|
@@ -710,6 +711,8 @@ No outcome to evaluate. Skip. | |
| 3. If the API returns rate-limit responses (`403` exhaustion or `429`), outcome evaluation workers **MUST** classify as `pending` and reschedule evaluation using the reset window. | ||
| 4. While any transient API failure condition exists, outcome evaluation workers **MUST NOT** emit `accepted` or `rejected` for label replacement state. | ||
|
|
||
| **Sync note:** Keep the API failure safeguards above aligned with [`replace-label-spec.md` Section 7](replace-label-spec.md#7-error-handling), which defines the shared `404`, `5xx`, and `429` REST failure semantics for `replace_label`. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 6b88f1c: the cross-reference is now backed by explicit 404 and 5xx/timeout/transport rules in |
||
|
|
||
| **References:** See [replace-label-spec.md](replace-label-spec.md) for the full definition of the `replace_label` safe-output type, including the message schema, processing model, and REST interface. | ||
|
|
||
| --- | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[/tdd] The "mid-flight blocklist change" test simulates mutation via a shared JS array reference, not an actual config-reload path. This means the test passes for the wrong reason: it's testing JS reference semantics rather than proving a real re-validation occurs before the write.
💡 Suggestion
The test works because
blockedis captured by reference in themain()closure and is mutated beforevalidateSingleLabelruns in the pre-write check. But this only holds if the implementation never copiesblockedbefore that check.A more robust test would set
blocked: ["done"]at config-load time with no mutation, proving the pre-write guard fires regardless of when the config was established:If the intent truly is to test mid-flight config reload, the handler would need to re-read config from an external source at write time — which the current implementation does not do.
@copilot please address this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in a47dd6e: the JS regression now models a config reload by replacing
config.blockedduring the second GET, and the handler re-reads current policy at the pre-write gate.