Summary
replace-island is reachable for update_issue but not for update_pull_request, even though the island-splicing logic lives in the shared body-update helper. The result is that a PR-body write can only be replace, append, or prepend — so any workflow that needs to update a section in place has to have the model regenerate the entire body, and a model that regenerates prose silently rewrites the parts it was not asked to touch.
Verified against main (latest release at time of writing: v0.86.2). Originally found on v0.81.6, and the gap is unchanged.
The asymmetry
pkg/workflow/js/safe_outputs_tools.json — operation enums:
| Tool |
operation enum |
update_issue |
replace, append, prepend, replace-island |
update_pull_request |
replace, append, prepend |
update_release |
replace, append, prepend |
pkg/parser/schemas/main_workflow_schema.json agrees on the workflow-config side: update-pull-request.operation is ['append','prepend','replace'], so a caller cannot set it as a default either.
Meanwhile replace-island is implemented and exercised — it appears in pkg/workflow/safe_outputs_validation_config.go, in actions/setup/js/fuzz_update_body_harness.cjs, and in the compiled lock files of several workflows in this repo. So this reads as an enum that was extended for issues and not for pull requests, rather than a deliberate restriction.
Why it matters
Marker-delimited splicing is the only operation in that set that is deterministic. replace hands the model the job of reproducing everything it is not changing, and that is not a job models do reliably.
Concretely, with update-pull-request + replace, across 23 successful writes by one workflow:
- 6 of 9 PRs lost human-authored content. Worst case: a 194-line description reduced to 74 — 167 lines gone.
- The destructive writes correlated with splicing into an existing section (7 of 7), not with body length. Pure appends were harmless (0 lines removed).
- Even non-lossy writes corrupted prose: bold markers moved so a sentence changed meaning, and hard-wrapped lines were reflowed.
- Length-based detection would not catch it — one body kept every heading and near-identical character count while paraphrasing paragraphs away.
There is no prompt-level fix for this. "Echo the body back verbatim" is an instruction to the same component that is failing, and a deterministic splice is the only thing that removes the model from the copy path.
Ask
Expose replace-island for update_pull_request — in both safe_outputs_tools.json and main_workflow_schema.json — matching update_issue. If the omission is deliberate for pull requests, saying so in the docs would help, since the helper's presence suggests otherwise.
Happy to open a PR if a maintainer confirms the direction.
Related
#19347 was the same shape on a different surface: the handler and validation schema supported a field that the compiled tool schema never exposed, so the agent could not set it.
Summary
replace-islandis reachable forupdate_issuebut not forupdate_pull_request, even though the island-splicing logic lives in the shared body-update helper. The result is that a PR-body write can only bereplace,append, orprepend— so any workflow that needs to update a section in place has to have the model regenerate the entire body, and a model that regenerates prose silently rewrites the parts it was not asked to touch.Verified against
main(latest release at time of writing: v0.86.2). Originally found on v0.81.6, and the gap is unchanged.The asymmetry
pkg/workflow/js/safe_outputs_tools.json—operationenums:operationenumupdate_issuereplace,append,prepend,replace-islandupdate_pull_requestreplace,append,prependupdate_releasereplace,append,prependpkg/parser/schemas/main_workflow_schema.jsonagrees on the workflow-config side:update-pull-request.operationis['append','prepend','replace'], so a caller cannot set it as a default either.Meanwhile
replace-islandis implemented and exercised — it appears inpkg/workflow/safe_outputs_validation_config.go, inactions/setup/js/fuzz_update_body_harness.cjs, and in the compiled lock files of several workflows in this repo. So this reads as an enum that was extended for issues and not for pull requests, rather than a deliberate restriction.Why it matters
Marker-delimited splicing is the only operation in that set that is deterministic.
replacehands the model the job of reproducing everything it is not changing, and that is not a job models do reliably.Concretely, with
update-pull-request+replace, across 23 successful writes by one workflow:There is no prompt-level fix for this. "Echo the body back verbatim" is an instruction to the same component that is failing, and a deterministic splice is the only thing that removes the model from the copy path.
Ask
Expose
replace-islandforupdate_pull_request— in bothsafe_outputs_tools.jsonandmain_workflow_schema.json— matchingupdate_issue. If the omission is deliberate for pull requests, saying so in the docs would help, since the helper's presence suggests otherwise.Happy to open a PR if a maintainer confirms the direction.
Related
#19347was the same shape on a different surface: the handler and validation schema supported a field that the compiled tool schema never exposed, so the agent could not set it.