feat(triage): generalize workflow-change block into block_auto_promotion - #1078
feat(triage): generalize workflow-change block into block_auto_promotion#1078rh-hemartin wants to merge 1 commit into
Conversation
PR Summary by QodoGeneralize triage auto-promotion blocking with reasons
AI Description
Diagram
High-Level Assessment
Files changed (9)
|
|
🤖 Finished Review · ✅ Success · Started 11:45 AM UTC · Completed 12:06 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $9.18 |
Code Review by Qodo
1. triage.md adds agent directives
|
|
Risk Assessment: elevated (3/5) DetailsScore 3 (elevated) preserved via re-review anchoring: Tier 1 signals are unchanged from the prior assessment (9 files, 389 lines in the 300-799 band, medium blast radius, 5 protected paths, test ratio 0.22, established non-bot author), and the head SHA delta since the last review is a single comment-only addition with no functional change. Tier 2 remains very elevated with extreme churn and fix/revert history concentrated in scripts/post-triage.sh (22 commits/30d, 45 fix/revert commits/90d, up to 10 distinct authors on docs/code.md), consistent with the prior review's Tier 2 measurement. No new signals (protected paths, dependency changes, security sensitivity) were introduced, so the composite score remains 3. Previous runRisk Assessment: elevated (3/5) DetailsScore 3 (elevated) preserved from prior assessment: Tier 1 signals are essentially unchanged (9 files, 387 lines in same 300-799 band, medium blast, 5 protected paths, test ratio 0.22), and Tier 2 remains very elevated with extreme fix/revert churn on post-triage.sh (55 all-time, 22 in 30d) and high multi-author contention (up to 10 distinct authors), partially offset by zero security sensitivity and no CI or dependency changes; composite 0.50x2.125 + 0.30x4.75 + 0.20x2.5 = 2.99 ~ 3. Previous run (2)Risk Assessment: elevated (3/5) DetailsElevated risk maintained at 3: 9 files with 361 changed lines and medium blast radius, 5 protected paths, test ratio 0.22. Very high fix/revert churn on post-triage.sh and multi-author contention keep Tier 2 elevated. Linked issue is well-scoped but Tier 2 churn signals sustain the elevated composite. Previous run (3)Risk Assessment: elevated (3/5) DetailsElevated risk maintained at 3, consistent with prior assessment: 9 files with 504 changed lines and large blast radius across the triage subsystem, 5 protected paths modified, test ratio at 0.22. Tier 2 continues to show very high fix/revert churn and high commit frequency, confirming an actively unstable area. Linked issue is well-scoped (priority/medium feature) which provides mild downward pressure, but the sustained Tier 2 churn keeps the composite at elevated. Previous run (4)Risk Assessment: elevated (3/5) DetailsElevated risk maintained at 3, consistent with prior assessment: 9 files with 504 changed lines and large blast radius across the triage subsystem, 5 protected paths modified, test ratio at 0.22. Tier 2 continues to show very high fix/revert churn and high commit frequency, confirming an actively unstable area. Linked issue is well-scoped (priority/medium feature) which provides mild downward pressure, but the sustained Tier 2 churn keeps the composite at elevated. Previous run (5)Risk Assessment: elevated (3/5) DetailsElevated risk (up from prior moderate): 9 files with 504 changed lines and large blast radius across the triage subsystem, 5 protected paths modified, test ratio at 0.22, and very high fix/revert churn (3-40 per file in 90d) indicating an actively unstable area; score increased from 2 to 3 due to blast radius upgrading to large and sustained high churn in Tier 2. Previous run (6)Risk Assessment: moderate (2/5) DetailsModerate risk: 9 files with 445 changed lines across the triage subsystem, 5 protected paths modified, no CI/dependency changes, established contributor, but elevated churn in the triage scripts and docs (20-36 commits in 30 days). |
ReviewFindingsMedium
Low
Next steps:
Previous runReviewFindingsHigh
Medium
Low
Next steps:
Previous run (2)ReviewFindingsMedium
Low
Previous run (3)ReviewFindingsMedium
Low
Previous run (4)ReviewFindingsMedium
Low
Previous run (5)ReviewFindingsMedium
Low
Previous run (6)ReviewFindingsHigh
Medium
Low
Next steps:
|
fa3c916 to
361fb43
Compare
|
🤖 Finished Review · ✅ Success · Started 12:42 PM UTC · Completed 1:02 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $6.68 |
Superseded by updated review
361fb43 to
f09b3d7
Compare
|
🤖 Finished Review · ✅ Success · Started 9:04 AM UTC · Completed 9:46 AM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high |
waynesun09
left a comment
There was a problem hiding this comment.
Five inline findings on scripts/post-triage.src.sh (1 high, 4 medium), all concerning the new fence stripper and the block_auto_promotion comment path.
f09b3d7 to
551a2af
Compare
|
🤖 Finished Review · ✅ Success · Started 5:31 AM UTC · Completed 5:51 AM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $7.60 |
551a2af to
0362b88
Compare
|
🤖 Finished Review · ✅ Success · Started 8:05 AM UTC · Completed 8:27 AM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $6.98 |
0362b88 to
e48f683
Compare
|
🤖 Finished Review · ✅ Success · Started 10:18 AM UTC · Completed 10:38 AM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $5.94 |
waynesun09
left a comment
There was a problem hiding this comment.
[HIGH] Required check functional-tests (triage) is currently failing (eval max_turns regression)
Live CI on the current HEAD (e48f683) shows functional-tests (triage) and the downstream functional-tests-complete gate both FAILING (run: https://github.com/fullsend-ai/agents/actions/runs/34110624836/job/101705837083), confirmed via gh pr checks 1078. The PR's test-plan checklist only lists the two passing shell unit-test scripts; the required live-agent eval suite is red with REGRESSIONS: 1 detected / [max_turns] pass_rate: >= 1.0 -> 0.875 — one triage eval case now needs more turns than the baseline allows, most plausibly from the added open-ended judgment call in the new block_auto_promotion prompt clause. No existing PR comment addresses this CI status; the PR is not mergeable in its current state.
Suggestion: Inspect the eval-results-triage artifact from that run to find the regressed case, and either tighten the prompt so the extra reasoning step isn't needed, or deliberately update the eval baseline (called out explicitly in the PR description) if the extra turn is expected. Do not merge while this required check is red.
Replace the single-purpose requires_workflow_changes boolean with a general-purpose block_auto_promotion object (blocked + reason). Workflow file detection now sets blocked=true with a reason; later gates can use the same field without a new post-script path. When blocked is true, the post-script appends the reason to the triage comment for every category. The label change (triaged instead of ready-to-code) still applies only to categories that would otherwise auto-promote. The reason is flattened for the comment (newlines and ANSI only) so :: and % stay intact for readers. Newlines cannot start a GHA workflow command on stdout. A held-for-review footer is used when auto-promotion is blocked. Signed-off-by: Hector Martinez <hemartin@redhat.com>
e48f683 to
5975344
Compare
|
🤖 Finished Review · ✅ Success · Started 7:18 AM UTC · Completed 7:38 AM UTC Commit: Runtime: pi · Model: sonnet → claude-sonnet-5 · Effort: high · Cost: $4.03 |
| "recommended_fix": { "type": "string", "minLength": 1 }, | ||
| "proposed_test_case": { "type": "string", "minLength": 1 }, | ||
| "requires_workflow_changes": { "type": "boolean" } | ||
| "block_auto_promotion": { |
There was a problem hiding this comment.
[medium] breaking-schema-change
The requires_workflow_changes boolean field remains removed from triage_summary and replaced with block_auto_promotion. Because triage_summary declares additionalProperties: false, any triage agent output still containing requires_workflow_changes is rejected by schema validation (covered by the requires-workflow-changes-rejected test). The producer (agents/triage.md), the schema, and the consumer (post-triage.sh/post-triage.src.sh) are all updated together in this one PR within this one repository, so a run binds all three from a single checkout - this is an intentional, coordinated flag-day rather than a staggered cross-repo break. Unchanged since the prior review of this PR (e48f683).
Suggested fix: Document in the PR description or release notes that this is an intentional flag-day change requiring atomic deployment. If staggered deployment is ever needed, add a transitional anyOf accepting either shape for one release cycle.
| @@ -569,6 +600,10 @@ run_test_no_pattern() { | |||
| echo "PASS: ${test_name}" | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
[low] scope-creep
The new test comment-fenced-code-block-preserved exercises the action: "insufficient" code path and asserts that a fenced code block in the general comment field survives unmangled. This does not trace to issue #2207 or to the block_auto_promotion work: the PR's only new sanitization helper, _comment_sanitize(), is applied exclusively to BLOCK_REASON under the action: "sufficient" branch and never touches COMMENT, so this test does not guard the new feature's sanitization behavior - it is an unrelated regression test added in the same commit. Unchanged since the prior review of this PR (e48f683).
waynesun09
left a comment
There was a problem hiding this comment.
Four inline findings (1 high, 3 medium) on the block_auto_promotion change: a missing breaking-change marker on the commit/PR title, a blank-reason gap in the post-script fallback, and two documentation issues.
| "recommended_fix": { "type": "string", "minLength": 1 }, | ||
| "proposed_test_case": { "type": "string", "minLength": 1 }, | ||
| "requires_workflow_changes": { "type": "boolean" } | ||
| "block_auto_promotion": { |
There was a problem hiding this comment.
[HIGH] Field removal ships without the ! suffix and BREAKING CHANGE: trailer that COMMITS.md requires
This PR removes the requires_workflow_changes boolean from triage_summary, which declares additionalProperties: false, so any output still carrying that field is now rejected. COMMITS.md:75-83 lists "A field, flag, command, or API endpoint is removed or renamed" as breaking; COMMITS.md:66-73 requires BOTH an ! after the type/scope AND a BREAKING CHANGE: trailer in the commit body; and COMMITS.md:115 says a missing ! must be flagged as an important-severity finding.
Verified at head 5975344: the PR title and the PR's single commit are both feat(triage): generalize workflow-change block into block_auto_promotion with no !, and the commit body has no BREAKING CHANGE: trailer.
This is distinct from the two existing bot threads on this line: those ask only for a note in the PR description or release notes, and the PR body already carries that note (line 15: "requires_workflow_changes is removed; the schema and post-script only accept block_auto_promotion") — which satisfies the bot while leaving the COMMITS.md commit/PR-title requirement unmet. No existing comment mentions the ! suffix, the trailer, or COMMITS.md.
Suggestion: Retitle the PR and amend the commit to feat(triage)!: generalize workflow-change block into block_auto_promotion, and add a trailer to the commit body, e.g.:
BREAKING CHANGE: triage_summary.requires_workflow_changes is removed and rejected by additionalProperties:false. Migrate requires_workflow_changes: true to block_auto_promotion: {"blocked": true, "reason": "..."}.
| AUTO_PROMOTION_BLOCKED=false | ||
| if [[ "${BLOCKED}" == "true" ]]; then | ||
| # Defense-in-depth: schema enforces minLength:1 but guard against unvalidated callers. | ||
| if [[ -z "${BLOCK_REASON}" ]]; then |
There was a problem hiding this comment.
[MEDIUM] Space- or tab-only block reason bypasses the "No reason provided" fallback and posts an empty blocked notice
_comment_sanitize (lines 77-79) replaces \n/\r with spaces, and the guard here tests [[ -z "${BLOCK_REASON}" ]], which does not match a non-empty but blank string.
Reproduced through the real pipeline (jq -r '...reason // empty' then _comment_sanitize) at head 5975344: reason "\n" IS caught (command substitution strips trailing newlines), but reason " ", "\t" and " \n" all bypass the fallback (len=1), and "\n\nx\n" yields " x". All of these satisfy the schema's minLength: 1 (schemas/triage-result.schema.json:206-211), so validation_loop passes them through, and the tracker comment renders **Auto-promotion blocked:** with nothing after it — the issue is held with no explanation, and the held-for-review footer still fires.
Not covered by an existing thread: the resolved thread at line 78 was about newline deletion vs replacement (fixed), and the bot thread on the bundled scripts/post-triage.sh:1403 argues the fallback is unreachable rather than that it fails to catch blank input.
Suggestion: Trim whitespace after sanitizing, e.g. BLOCK_REASON="$(_comment_sanitize "${BLOCK_REASON}" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')", or change the guard to [[ -z "${BLOCK_REASON//[[:space:]]/}" ]] so the "No reason provided" fallback still fires. Add a fixture with a space-only reason to scripts/post-triage-test.sh, and re-run make script-build so the bundled scripts/post-triage.sh mirror (around line 1403) stays in sync.
| | Label | Meaning | | ||
| |-------|---------| | ||
| | `ready-to-code` | Triggers the code agent. Applied by the [triage](triage.md) agent for low-risk categories (bug, documentation, performance), or manually by a human for feature work after prioritization. Not applied when the triage result sets `requires_workflow_changes`, since the code agent cannot modify workflow files. | | ||
| | `ready-to-code` | Triggers the code agent. Applied by the [triage](triage.md) post-script for low-risk categories (bug, documentation, performance) when auto-promotion is not blocked, or manually by a human for feature work or workflow changes after review. | |
There was a problem hiding this comment.
[MEDIUM] This row now tells humans to hand-apply ready-to-code for workflow changes, dispatching a coder that provably cannot push them
The rewritten ready-to-code row reads "...or manually by a human for feature work or workflow changes after review." The row it replaced said the opposite — "Not applied when the triage result sets requires_workflow_changes, since the code agent cannot modify workflow files" — so this PR deletes the warning and replaces it with the inverse instruction.
The permission gap itself is unchanged, and is asserted in two places, one of them edited by this same PR: agents/triage.md:133 still instructs the agent to block because "the code agent cannot modify workflow files under current permissions", and scripts/validate-code-output.sh:204-205,239 detects the push rejection and posts "the coder app intentionally lacks workflows write permission. Changes to .github/workflows/ must be made outside the agent".
Applying ready-to-code to a workflow-change issue therefore dispatches a run guaranteed to fail at push time — the exact outcome block_auto_promotion exists to prevent. No existing comment touches docs/code.md; this is about the docs inversion, not the footer wording discussed on the won't-fix thread in post-triage.src.sh.
Suggestion: Drop "or workflow changes" from the row and restore an explicit caveat, e.g. "Workflow/CI file changes cannot be completed by the code agent (it lacks workflows write permission) and require a human-authored PR — do not apply ready-to-code to unblock them."
| post-script applies the actions via `PUT /rest/api/3/issue/{key}` with | ||
| `fields.components`. | ||
|
|
||
| ### Blocking auto-promotion |
There was a problem hiding this comment.
[MEDIUM] The /fs-code -> held-for-review footer swap is undocumented, and unlike the label change it fires for every category
When BLOCKED == "true", scripts/post-triage.src.sh:756-771 replaces the standard **Next steps:** /fs-code ... footer with "This issue was held for review. Run /fs-code only after confirming the concerns above." That footer branches on BLOCKED directly, so it applies to every category — including feature/security/other, which never auto-promoted and are otherwise unaffected by this change.
The new documentation stops one sentence short of it: this "Blocking auto-promotion" section (lines 164-177) documents only the reason-append and the label substitution, and agents/triage.md:389-395 does the same. Verified at head 5975344 — grep for "held for review", "Next steps" and "footer" across docs/triage.md, docs/code.md and agents/triage.md returns zero hits.
An operator reading the docs would conclude feature/security issues are untouched by blocked: true, when in fact their comment loses the /fs-code invitation.
Suggestion: Add a bullet to the "Blocking auto-promotion" section, e.g. "blocked: true also replaces the standard /fs-code next-steps footer with a held-for-review note. Unlike the label change, this applies to every category, including feature/security/other."
Summary
Replace the single-purpose
requires_workflow_changesboolean with a general-purposeblock_auto_promotionobject (blocked+reason). Workflow-file detection now uses that field so later gates (effort scoring) can reuse the same post-script path and attach a reason.This is the first of two slices split from
feat/2207-effort-gating. Effort estimation is not in this PR.The prompt's second block condition ("any other condition where auto-dispatch would be premature") is deliberately provisioned ahead of the effort-scoring gate (fullsend#2207 / follow-up PR) so reviewers and agents do not need to guess the intent. This PR only implements the workflow-file gate; the second clause is the hook for that follow-up.
Ref fullsend-ai/fullsend#2207
Behavior
blockedis true, the post-script appends**Auto-promotion blocked:** <reason>to the triage comment for every category.blocked=truealso appliestriagedinstead ofready-to-code. Categories that already go totriaged(feature, security, other) keep that label.requires_workflow_changesis removed; the schema and post-script only acceptblock_auto_promotion.::and%stay intact. A held-for-review footer is used wheneverblockedis true, including feature andTRIAGE_AUTO_CODE=off.Test plan
bash scripts/post-triage-test.shbash scripts/validate-output-schema-test.sh