Skip to content

feat(triage): generalize workflow-change block into block_auto_promotion - #1078

Open
rh-hemartin wants to merge 1 commit into
mainfrom
feat/2207-block-auto-promotion
Open

feat(triage): generalize workflow-change block into block_auto_promotion#1078
rh-hemartin wants to merge 1 commit into
mainfrom
feat/2207-block-auto-promotion

Conversation

@rh-hemartin

@rh-hemartin rh-hemartin commented Aug 28, 2026

Copy link
Copy Markdown
Member

Summary

Replace the single-purpose requires_workflow_changes boolean with a general-purpose block_auto_promotion object (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.

Ref fullsend-ai/fullsend#2207

Behavior

  • When blocked is true, the post-script appends **Auto-promotion blocked:** <reason> to the triage comment for every category.
  • For bug/documentation/performance, blocked=true also applies triaged instead of ready-to-code. Categories that already go to triaged (feature, security, other) keep that label.
  • requires_workflow_changes is removed; the schema and post-script only accept block_auto_promotion.
  • The reason is flattened for the comment (newlines and ANSI only) so :: and % stay intact. A held-for-review footer is used when auto-promotion is blocked.

Test plan

  • bash scripts/post-triage-test.sh
  • bash scripts/validate-output-schema-test.sh

@rh-hemartin
rh-hemartin requested a review from a team as a code owner August 28, 2026 11:43
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Generalize triage auto-promotion blocking with reasons

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Replaces the workflow-specific flag with reusable, reasoned auto-promotion blocking.
• Preserves legacy outputs while routing blocked auto-code categories to human review.
• Sanitizes triage comments and expands schema and post-script regression coverage.
Diagram

graph TD
  A["Triage agent"] --> B["Result schema"] --> C["Post-triage script"] --> D{"Auto-code eligible?"}
  D -->|Yes| E{"Promotion blocked?"}
  D -->|No| G["Triaged"]
  E -->|No| F["Ready to code"]
  E -->|Yes| G
  E -.->|Reason| H["Held comment"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. List independent blockers
  • ➕ Represents multiple simultaneous gates without merging reasons
  • ➕ Allows future gates to add structured blocker types
  • ➖ Expands the schema and prompt contract prematurely
  • ➖ Requires aggregation and compatibility logic in the post-script
2. Keep gate-specific fields
  • ➕ Makes each blocking condition explicit
  • ➕ Minimizes change to the existing workflow-specific path
  • ➖ Duplicates routing logic as new gates arrive
  • ➖ Couples post-processing to every new triage policy

Recommendation: Use the PR's single generalized block object for this slice: it centralizes routing, carries a human-readable reason, and preserves one-release compatibility without over-designing future effort gating. Consider a blocker array only if multiple independent producers must report concurrent reasons.

Files changed (9) +364 / -82

Enhancement (3) +179 / -50
triage.mdTeach triage to emit reasoned promotion blocks +13/-4

Teach triage to emit reasoned promotion blocks

• Updates workflow-change detection and output examples to use block_auto_promotion. Clarifies category routing and instructs the agent to avoid fenced code blocks in comments.

agents/triage.md

post-triage.shBundle generalized auto-promotion gate behavior +83/-23

Bundle generalized auto-promotion gate behavior

• Updates the generated post-triage script to resolve the new gate with legacy fallback, route eligible blocked issues to triaged, sanitize untrusted text, and publish held-review guidance.

scripts/post-triage.sh

post-triage.src.shImplement generalized auto-promotion gating +83/-23

Implement generalized auto-promotion gating

• Introduces the source implementation for reasoned blocking, backward-compatible workflow handling, comment fence removal, label routing, and blocked-specific next steps.

scripts/post-triage.src.sh

Tests (2) +149 / -25
post-triage-test.shCover blocked routing and comment sanitization +115/-25

Cover blocked routing and comment sanitization

• Expands regression coverage for category routing, legacy fallback, reason footers, held-review guidance, workflow-command sanitization, and fenced-block removal.

scripts/post-triage-test.sh

validate-output-schema-test.shValidate the new promotion block schema +34/-0

Validate the new promotion block schema

• Adds valid and invalid cases for required fields, empty reasons, extra properties, omission behavior, and deprecated-field compatibility.

scripts/validate-output-schema-test.sh

Documentation (3) +22 / -6
FEATURES.mdUpdate feature guidance for generalized promotion blocks +1/-1

Update feature guidance for generalized promotion blocks

• Replaces the workflow-specific schema example with the reusable block_auto_promotion field.

FEATURES.md

code.mdClarify ready-to-code promotion behavior +1/-1

Clarify ready-to-code promotion behavior

• Documents that the triage post-script applies ready-to-code only when auto-promotion is not blocked.

docs/code.md

triage.mdDocument auto-promotion blocking and compatibility +20/-4

Document auto-promotion blocking and compatibility

• Explains blocked and unblocked outcomes, affected categories, reason handling, and the temporary requires_workflow_changes fallback.

docs/triage.md

Other (1) +14 / -1
triage-result.schema.jsonAdd the block_auto_promotion result contract +14/-1

Add the block_auto_promotion result contract

• Adds a strict blocked-and-reason object with reason length validation. Retains requires_workflow_changes as an optional deprecated property for one release.

schemas/triage-result.schema.json

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 28, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:45 AM UTC · Completed 12:06 PM UTC

Commit: fa3c916 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $9.18

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (2)

Grey Divider


Action required

1. triage.md adds agent directives 📜 Skill insight ⛨ Security
Description
The changed prompt text directly instructs the triage agent to set and apply block_auto_promotion.
This introduces agent-instruction patterns in a changed configuration/prompt file, which the
checklist expressly prohibits.
Code

agents/triage.md[R383-385]

+**Blocking auto-promotion:** Use the `block_auto_promotion` field in `triage_summary` to prevent the post-triage script from auto-promoting the issue to the code agent. Set `blocked: true` with a `reason` when:
+- The fix requires modifying CI/pipeline configuration files (`.github/workflows/`, `.gitlab-ci.yml`, `.fullsend/.github/workflows/`, or enrolled-repo shim workflows) that the code agent cannot modify under current permissions.
+- Any other condition where auto-dispatch would be premature.
Relevance

●●● Strong

Prompt-instruction compliance findings are accepted when changed agent guidance contains imperative
directives.

PR-#381
PR-#326

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538322 prohibits agent-instruction patterns in changed comments, string literals,
or configuration values. The added text uses imperative directives such as `Use the
block_auto_promotion field and Set blocked: true`, directly matching the prohibited pattern.

agents/triage.md[383-389]
Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The changed `agents/triage.md` content contains direct agent instructions, contrary to PR Compliance ID 1538322.

## Issue Context
The added text directs the agent to use `block_auto_promotion` and specifies when to set it. Rework the change so prohibited instruction patterns are not introduced in comments, strings, or configuration values.

## Fix Focus Areas
- agents/triage.md[383-389]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. False block still falls back 🐞 Bug ≡ Correctness
Description
Using jq's // empty turns an explicit block_auto_promotion.blocked: false into an empty result,
so a simultaneously present deprecated requires_workflow_changes: true incorrectly blocks
promotion. This violates the documented rule that the deprecated field is consulted only when
block_auto_promotion is absent.
Code

scripts/post-triage.src.sh[R424-426]

+    BLOCKED=$(jq -r '.triage_summary.block_auto_promotion.blocked // empty' "${RESULT_FILE}")
+    BLOCK_REASON=$(jq -r '.triage_summary.block_auto_promotion.reason // empty' "${RESULT_FILE}")
+    if [[ -z "${BLOCKED}" ]]; then
Relevance

●●● Strong

This is a deterministic jq false-value fallback bug, matching accepted requests for robust
null/boolean handling.

PR-#284
PR-#876

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The schema allows both fields and allows blocked to be false, while the documentation limits
legacy fallback to absence of the new object. In jq, false // empty emits no value, so the
subsequent empty check enters the legacy fallback and changes the result to true.

scripts/post-triage.src.sh[424-435]
schemas/triage-result.schema.json[200-212]
docs/triage.md[150-157]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An explicit `block_auto_promotion.blocked: false` is lost because jq's `//` treats `false` like a missing value, allowing the deprecated flag to override it.

## Issue Context
The schema permits both migration fields, while the documented precedence says `requires_workflow_changes` applies only when the new object is absent. Test the valid combination `blocked:false` plus `requires_workflow_changes:true`.

## Fix Focus Areas
- scripts/post-triage.src.sh[424-436]
- scripts/post-triage-test.sh[810-818]
- scripts/post-triage.sh[1349-1361]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Protected automation paths modified 📜 Skill insight § Compliance
Description
This PR modifies protected agents/ and scripts/ paths, including triage instructions and
executable automation. These governance/automation changes require human approval and must not be
auto-approved.
Code

scripts/post-triage.src.sh[R73-76]

+# Drop paired line-start fenced code blocks. An unmatched opener is left
+# in place so the remainder of the text is not deleted. Inline triple
+# backticks (not at line start) are left unchanged.
+strip_line_start_fences() {
Relevance

●● Moderate

Protected-path approval findings are mixed: some governance requests were rejected, while others
remain undetermined.

PR-#609
PR-#476
PR-#157

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538392 mandates a finding whenever protected paths such as agents/ or scripts/
are modified. The diff adds executable processing logic under scripts/ and also changes
agents/triage.md, so the protected-path review requirement is triggered.

scripts/post-triage.src.sh[73-76]
agents/triage.md[133-133]
Skill: pr-review



Remediation recommended

4. Fence stripping is bypassable 🐞 Bug ≡ Correctness
Description
The sanitizer recognizes every line beginning with exactly three backticks as a delimiter, so valid
tilde-fenced blocks are left intact and backtick-prefixed content inside a longer/matching fence can
be mistaken for its close. Consequently the post-script can publish code content it claims to strip,
or delete the wrong region of the triage comment.
Code

scripts/post-triage.src.sh[R80-87]

+      if ($0 ~ /^```/) {
+        n++
+        pos[n] = NR
+      }
+    }
+    END {
+      for (i = 1; i + 1 <= n; i += 2) {
+        for (j = pos[i]; j <= pos[i + 1]; j++) skip[j] = 1
Relevance

●●● Strong

Fence parsing edge cases are accepted; historical linter fixes specifically broaden fence
recognition and handling.

PR-#476

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The agent contract prohibits fenced code blocks generally, but the implementation records only lines
matching ^``` and blindly removes alternating pairs. The same helper's output becomes the posted
comment, so unsupported or prematurely closed fences reach every tracker.

agents/triage.md[367-372]
scripts/post-triage.src.sh[76-97]
scripts/post-triage.src.sh[489-495]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new sanitizer only detects `^```` and pairs matches by position rather than matching actual Markdown opening and closing delimiters. Valid fenced blocks can survive, and delimiter-like lines inside a block can terminate removal early.

## Issue Context
Track the opening delimiter character and length, accept both backtick and tilde fences, and only close on a compatible delimiter-only line. Add tests for tilde fences, longer backtick fences containing triple backticks, and non-closing backtick-prefixed lines.

## Fix Focus Areas
- scripts/post-triage.src.sh[76-97]
- scripts/post-triage-test.sh[608-618]
- scripts/post-triage.sh[1001-1022]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 56 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review
Review mode: ⚖️ Balanced

Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread scripts/post-triage.src.sh Outdated
Comment thread agents/triage.md
Comment thread scripts/post-triage.src.sh Outdated
Comment thread scripts/post-triage.src.sh Outdated
@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Aug 28, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 28, 2026

Copy link
Copy Markdown

Risk Assessment: elevated (3/5)

Details

Elevated 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

Risk Assessment: elevated (3/5)

Details

Elevated 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 (2)

Risk Assessment: elevated (3/5)

Details

Elevated 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 (3)

Risk Assessment: elevated (3/5)

Details

Elevated 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 (4)

Risk Assessment: moderate (2/5)

Details

Moderate 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).

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [protected-path] agents/triage.md, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh — Five files under protected paths (agents/, scripts/) are modified. The PR links to dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207 and describes the rationale for these changes (generalizing the auto-promotion blocking mechanism). Human approval is always required for protected-path changes, regardless of context.

Low

  • [behavioral-change] scripts/post-triage.sh, scripts/post-triage.src.sh — The old code unconditionally emitted ::warning::Triage detected workflow file changes required (#325) whenever requires_workflow_changes was true, regardless of AUTO_CODE_ALLOWED. The new code only emits the warning when both BLOCKED=true and AUTO_CODE_ALLOWED=true. When auto-code is disabled and blocked=true, no warning is logged — an intentional narrowing of observability scope.
Previous run

Review

Findings

Medium

  • [protected-path] agents/triage.md, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh — Five files under protected paths (agents/, scripts/) are modified. The PR links to dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207 and describes the rationale for these changes (generalizing the auto-promotion blocking mechanism). Human approval is always required for protected-path changes, regardless of context.

Low

  • [logic-inconsistency] scripts/post-triage.sh, scripts/post-triage.src.sh — The fence-stripping guard for BLOCK_REASON checks only backtick fences (grep -q '^\``') but not tilde fences (~~~), while the equivalent guard for COMMENT correctly checks both (grep -qE '^(```|~~~)'). In practice this is harmless because _gha_sanitize()` strips newlines first, making multi-line fences impossible — the guard is effectively dead code after sanitization.

  • [deprecation-lifecycle] schemas/triage-result.schema.json — The deprecation comment for requires_workflow_changes says "Kept optional for one release" but no tracking mechanism (issue reference, version target) ensures removal. A follow-up issue would prevent the dual-path logic from becoming permanent.

  • [behavioral-change] scripts/post-triage.sh, scripts/post-triage.src.sh — The old code unconditionally emitted ::warning::Triage detected workflow file changes required (#325) whenever requires_workflow_changes was true, regardless of AUTO_CODE_ALLOWED. The new code only emits the warning when both BLOCKED=true and AUTO_CODE_ALLOWED=true. When auto-code is disabled and blocked=true, no warning is logged — an intentional narrowing of observability scope.

Previous run (2)

Review

Findings

Medium

  • [protected-path] agents/triage.md, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh — Five files under protected paths (agents/, scripts/) are modified. The PR links to dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207 and describes the rationale for these changes (generalizing the auto-promotion blocking mechanism). Human approval is always required for protected-path changes, regardless of context.

Low

  • [logic-inconsistency] scripts/post-triage.sh, scripts/post-triage.src.sh — The fence-stripping guard for BLOCK_REASON checks only backtick fences (grep -q '^\``') but not tilde fences (~~~), while the equivalent guard for COMMENT correctly checks both (grep -qE '^(```|~~~)'). In practice this is harmless because _gha_sanitize()` strips newlines first, making multi-line fences impossible — the guard is effectively dead code after sanitization.

  • [schema-runtime-mismatch] scripts/post-triage-test.sh — The schema enforces reason with minLength: 1, but the post-script has a fallback for empty BLOCK_REASON ("No reason provided"). The test blocked-empty-reason-gets-fallback exercises a state the schema disallows. This is defense-in-depth; a brief comment noting it exercises a schema-invalid state for robustness would clarify intent.

  • [deprecation-lifecycle] schemas/triage-result.schema.json — The deprecation comment for requires_workflow_changes says "Kept optional for one release" but no tracking mechanism (issue reference, version target) ensures removal. A follow-up issue would prevent the dual-path logic from becoming permanent.

  • [precedence-semantics] scripts/post-triage.sh — When both block_auto_promotion and requires_workflow_changes are present, block_auto_promotion wins unconditionally via has(). This precedence rule is correctly tested (unblocked-wins-over-deprecated-workflow-flag) but only documented in inline comments — no schema-level annotation communicates it to external consumers.

Previous run (3)

Review

Findings

Medium

  • [protected-path] agents/triage.md, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh — Five files under protected paths (agents/, scripts/) are modified. The PR links to dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207 and describes the rationale for these changes (generalizing the auto-promotion blocking mechanism). Human approval is always required for protected-path changes, regardless of context.

Low

  • [logic-inconsistency] scripts/post-triage.sh — The fence-stripping guard for BLOCK_REASON checks only backtick fences (grep -q '^\``') but not tilde fences (~~~), while the equivalent guard for COMMENT correctly checks both (grep -qE '^(```|~~~)'). In practice this is harmless because _gha_sanitize()strips newlines first, making multi-line fences impossible, but the inconsistency is worth aligning. The same applies toscripts/post-triage.src.sh`.

  • [scope-creep] agents/triage.md — The PR changes the comment field instructions from “Include the proposed test case as a fenced code block” to “Do not include fenced code blocks; summarize test cases and fixes in prose.” This behavioral change is reinforced by the new strip_line_start_fences() function and ~15 associated tests, but is not mentioned in the PR title or description.

  • [code-duplication] scripts/post-triage.sh, scripts/post-triage.src.sh — Both files now contain identical copies of strip_line_start_fences(), the block_auto_promotion gate logic (~40 lines), and the held-for-review footer, extending a pre-existing duplication pattern.

  • [deprecation-lifecycle] schemas/triage-result.schema.json — The deprecation comment for requires_workflow_changes says “Kept optional for one release” but no tracking mechanism ensures removal. A follow-up issue would prevent the dual-path logic from becoming permanent.

  • [schema-runtime-mismatch] scripts/post-triage.sh — The schema enforces reason with minLength: 1, but the post-script has a fallback for empty BLOCK_REASON ("No reason provided"). The test blocked-empty-reason-gets-fallback exercises a state the schema disallows. This is defense-in-depth — adding a comment would clarify the intent.

Previous run (4)

Review

Findings

High

  • [logic-error] scripts/post-triage.sh:1345 — jq's // (alternative operator) treats false as falsy, so jq -r '.triage_summary.block_auto_promotion.blocked // empty' produces no output when blocked is false. This causes BLOCKED to be empty, falling into the if [[ -z "${BLOCKED}" ]] branch which reads the deprecated requires_workflow_changes field instead. If both fields are present with conflicting values (e.g., block_auto_promotion.blocked: false and requires_workflow_changes: true), the deprecated field incorrectly wins. The same issue exists in scripts/post-triage.src.sh.
    Remediation: Check for the presence of the block_auto_promotion object rather than the truthiness of blocked. Use jq -r 'if .triage_summary | has("block_auto_promotion") then "yes" else "no" end' and branch on that, then use .blocked | tostring inside.

  • [protected-path] agents/triage.md, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh — Five files under protected paths (agents/, scripts/) are modified. The PR has no linked issue, so there is insufficient context to verify authorization for modifying governance and infrastructure files. Human approval is required for protected-path changes.

Medium

  • [missing-authorization] — The PR body references branch feat/2207-effort-gating and mentions this is the first of two slices, but no issue is linked (no Closes #N or Ref #N). Per AGENTS.md section 3, non-trivial changes (400+ lines across 9 files) require explicit authorization via a linked issue.
    Remediation: Link the authorizing issue using Closes #NNNN or Ref #NNNN.

  • [sanitization-pattern] scripts/post-triage.src.shBLOCK_REASON is sanitized for :: sequences via a while-loop, but is not passed through _gha_sanitize() like other untrusted values in this file. While BLOCK_REASON does not currently reach stdout (it flows into COMMENT which is posted via --body-file), the inconsistency with the file's sanitization patterns creates fragility for future maintainers. The same applies to scripts/post-triage.sh.
    Remediation: Either pass BLOCK_REASON through _gha_sanitize() or add a comment explaining why the colon-only sanitization is sufficient.

Low

  • [scope-creep] agents/triage.md:367 — The PR changes the comment field instructions from "Include the proposed test case as a fenced code block" to "Do not include fenced code blocks; summarize test cases and fixes in prose." This is related to the new strip_line_start_fences() function (both are about preventing fenced blocks in comments), but the behavioral change to agent commenting style is not mentioned in the PR title or description.

  • [test-inadequate] scripts/post-triage-test.sh — No test covers the scenario where both block_auto_promotion.blocked: false and requires_workflow_changes: true are present. This would exercise the jq false // empty precedence bug and document the intended field priority.

  • [test-inadequate] scripts/post-triage-test.sh — No test verifies the conditional "Next steps" footer text for blocked issues ("This issue was held for review. Run /fs-code only after confirming the concerns above.").


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 28, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:42 PM UTC · Completed 1:02 PM UTC

Commit: 361fb43 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $6.68

@fullsend-ai-review fullsend-ai-review Bot added risk/elevated PR risk: elevated and removed risk/moderate PR risk: moderate labels Aug 28, 2026
@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 28, 2026 13:02

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 28, 2026
@rh-hemartin
rh-hemartin force-pushed the feat/2207-block-auto-promotion branch from 361fb43 to f09b3d7 Compare September 1, 2026 09:02
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 1, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:04 AM UTC · Completed 9:46 AM UTC

Commit: f09b3d7 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread scripts/post-triage.src.sh Outdated
Comment thread scripts/post-triage.src.sh Outdated
Comment thread scripts/post-triage.src.sh Outdated
Comment thread scripts/post-triage.src.sh Outdated
Comment thread scripts/post-triage.src.sh Outdated
@rh-hemartin
rh-hemartin force-pushed the feat/2207-block-auto-promotion branch from f09b3d7 to 551a2af Compare September 3, 2026 05:30
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 3, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:31 AM UTC · Completed 5:51 AM UTC

Commit: 551a2af · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $7.60

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>
@rh-hemartin
rh-hemartin force-pushed the feat/2207-block-auto-promotion branch from 551a2af to 0362b88 Compare September 3, 2026 08:04
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 3, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:05 AM UTC · Completed 8:27 AM UTC

Commit: 0362b88 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $6.98


if [[ "${ACTION}" == "sufficient" ]]; then
COMMENT="${COMMENT}
if [[ "${AUTO_PROMOTION_BLOCKED}" == "true" ]]; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[HIGH] Held-for-review footer still keys off AUTO_PROMOTION_BLOCKED, so it doesn't fire for feature/off-auto-code blocked cases despite the body now saying 'Auto-promotion blocked'

Lines 448-461: when BLOCKED=="true" the reason is now appended to COMMENT unconditionally (fixed per the resolved thread at old line 494, where the maintainer noted 'Appending always regardless of AUTO_CODE_ALLOWED'), but AUTO_PROMOTION_BLOCKED is still only set to true inside the nested if [[ "${AUTO_CODE_ALLOWED}" == "true" ]] branch. The footer logic here branches on AUTO_PROMOTION_BLOCKED, not BLOCKED: if [[ "${AUTO_PROMOTION_BLOCKED}" == "true" ]] prints the held-for-review footer, else it prints the normal '/fs-code — agent creates a PR to implement this issue' footer. So for category=feature (never in TRIAGE_AUTO_CODE_CATEGORIES by default, so AUTO_CODE_ALLOWED stays false) or TRIAGE_AUTO_CODE=off with any category, a blocked issue's comment body says 'Auto-promotion blocked: ' and is immediately followed by the standard footer inviting /fs-code — a self-contradicting comment on the tracker. The PR description states as fact: 'A held-for-review footer is used when auto-promotion is blocked' — true only for the subset of categories that would otherwise auto-promote. Confirmed untested: the existing blocked-feature-block-reason-in-comment test (post-triage-test.sh) only asserts the 'Auto-promotion blocked:' substring is present, never what footer follows; the only footer-content test (blocked-held-for-review-footer) uses category=bug, which does get AUTO_PROMOTION_BLOCKED=true and so never exercises the broken path. This is the same substance flagged in the suggestion text of the now-resolved review thread at scripts/post-triage.src.sh:494 ('...use the held-for-review footer instead of the /fs-code invitation in the TRIAGE_AUTO_CODE=off case'), but that thread's fix only addressed the body-append half of the suggestion — the footer half was never fixed, and the bug lives on a different code path over 250 lines away.

Suggestion: Drive the held-for-review footer off BLOCKED=="true" directly (or a dedicated 'reason was appended to the comment' flag set unconditionally alongside the body append), not off AUTO_PROMOTION_BLOCKED/AUTO_CODE_ALLOWED. Add a footer-content assertion (not just body-substring) to the feature-blocked and TRIAGE_AUTO_CODE=off test cases to lock in the fix.

# Auto-promotion gate (#325): the triage agent can block auto-promotion
# via block_auto_promotion.blocked (e.g., workflow file changes). When
# blocked, the reason is appended to the comment. Bug/docs/performance
# categories receive triaged instead of ready-to-code. tostring is used

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Code comment misstates jq's // semantics — tostring adds nothing here

The comment reads: 'tostring is used so an explicit blocked:false is not treated as missing (jq's // would).' Empirically disproved with jq 1.7: echo '{"blocked": false}' | jq -r '.blocked // false' and the same piped through | tostring both print false; jq's // only substitutes on null/false-y absent values in the sense of jq truthiness, and -r already prints booleans unquoted, so tostring changes nothing here (also confirmed for blocked:true and the missing-field case). No functional impact — the runtime check at [[ "${BLOCKED}" == "true" ]] behaves identically either way — but the comment states an incorrect tool-behavior claim as fact and will mislead a future maintainer editing this line (e.g. into thinking tostring is load-bearing when it is not, or removing it for the wrong reason).

Suggestion: Correct or remove the comment. If tostring is kept defensively (e.g. in case schema validation is bypassed and a non-boolean value slips through), say that explicitly instead of citing the incorrect // semantic.

# stdout (_gha_sanitize). An embedded newline is flattened so it cannot
# start a workflow command if a later stdout echo is added.
_comment_sanitize() {
printf '%s' "$1" | tr -d '\n\r' | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] _comment_sanitize deletes embedded newlines instead of replacing them with a separator, garbling multi-sentence block reasons

_comment_sanitize() { printf '%s' "$1" | tr -d '\n\r' | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g'; } is applied to BLOCK_REASON before it's appended into the tracker comment body. tr -d '\n\r' deletes newlines outright rather than replacing them with whitespace, so an agent-authored reason like "Line 1.\nLine 2." is posted as "Line 1.Line 2." with words run together and no separator — the schema (schemas/triage-result.schema.json) allows arbitrary strings up to 1024 chars in this field, including newlines, and nothing upstream forbids multi-line reasons. This is a new, distinct issue from the earlier resolved thread about percent-encoding on this code path (which concerned _gha_sanitize, not the newline-deletion behavior of the newer _comment_sanitize); no existing PR comment addresses the missing separator.

Suggestion: Replace newlines with a space instead of deleting them, e.g. tr '\n\r' ' ', which keeps the defense-in-depth intent (no embedded newline that could start a workflow command on a later stdout echo) while keeping multi-line reasons readable. Markdown would collapse a literal newline into a soft-wrap space anyway, so this is strictly safer with no loss.

Comment thread agents/triage.md
**Workflow change detection (optional):** If the issue likely requires modifying CI/pipeline configuration files (`.github/workflows/`, `.gitlab-ci.yml`, `.fullsend/.github/workflows/`, or enrolled-repo shim workflows), set `requires_workflow_changes: true` in `triage_summary`. When set, the post-triage script skips auto-triggering the code agent because the code agent cannot modify workflow files under current permissions. The triage comment should warn about this limitation and note that manual intervention is required. When `requires_workflow_changes` is not set or is `false`, auto-triggering proceeds normally.
**Blocking auto-promotion:** Use the `block_auto_promotion` field in `triage_summary` to prevent the post-triage script from auto-promoting the issue to the code agent. Set `blocked: true` with a `reason` when:
- The fix requires modifying CI/pipeline configuration files (`.github/workflows/`, `.gitlab-ci.yml`, `.fullsend/.github/workflows/`, or enrolled-repo shim workflows) that the code agent cannot modify under current permissions.
- Any other condition where auto-dispatch would be premature.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Triage prompt's block condition includes an unbounded 'any other condition' clause broader than the one concrete gate this PR ships

The prompt instructs the agent to set blocked: true when '(1) The fix requires modifying CI/pipeline configuration files...' OR '(2) Any other condition where auto-dispatch would be premature.' Clause (2) is open-ended and, unlike clause (1), maps to no concrete gate implemented in this PR — the PR description frames the generalization as groundwork for a later effort-scoring gate (fullsend#2207/#1079), not as a currently-defined second condition. There is no test exercising this open-ended clause, and no evaluation data cited constraining what the agent will treat as 'premature.' This is not a functional bug — it's a scope/precision gap: shipping an unbounded instruction ahead of the concrete gate that would justify it risks the agent over-applying blocked:true (regressing auto-promotion for bug/documentation/performance) in ways this PR's test suite cannot catch, since the test suite only exercises the concrete workflow-file case.

Suggestion: Scope the instruction to the concrete workflow-file case for this PR and introduce the broader 'any other premature condition' clause in the follow-up PR (#1079) once there is a second concrete gate to anchor it to, or keep it but note in the PR description that it's deliberately provisioned ahead of the effort-scoring gate so reviewers/agents don't need to guess the intent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment risk/elevated PR risk: elevated

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants