Skip to content

feat(triage): effort-estimation skill and auto-promotion gate - #646

Closed
rh-hemartin wants to merge 1 commit into
mainfrom
feat/2207-effort-gating
Closed

feat(triage): effort-estimation skill and auto-promotion gate#646
rh-hemartin wants to merge 1 commit into
mainfrom
feat/2207-effort-gating

Conversation

@rh-hemartin

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

Copy link
Copy Markdown
Member

Summary

  • Add effort-estimation skill that scores implementation effort on a 1-5 scale across scope, testing, domain knowledge, and risk. Issues scoring >= 4, or with any single dimension at 5, block auto-promotion.
  • Replace requires_workflow_changes boolean with block_auto_promotion object ({blocked, reason}) in triage_summary. The schema conditionally requires this field for auto-promotable categories (bug, documentation, performance). The post-script gates ready-to-code vs triaged accordingly.
  • Keep requires_workflow_changes as a deprecated optional property for one release so lockstep-skewed agent output still validates.
  • Skill documents its relationship to the prioritize agent's RICE Effort dimension (different purpose: synchronous binary gate vs asynchronous continuous ranking).
  • Harden the post-script against workflow-command injection: ::warning:: uses a fixed message (no interpolation of untrusted content), and BLOCK_REASON is sanitized (:: collapsed to :) and fence-stripped before any use.
  • Fenced-code stripping in comments and BLOCK_REASON only drops paired line-start fences; unmatched openers and inline triple backticks are left in place.
  • The /fs-code action-hints footer is conditional: when auto-promotion is blocked, it warns the user to confirm concerns first instead of offering one-click dispatch.
  • Switch the comment field to prose-only (no fenced code blocks) to avoid model abbreviation of identifiers inside JSON strings.
  • Add eval cases 009 (high-effort multi-component), 010 (low-effort single-file), and 011 (mixed profile: Risk=5, other dimensions low). Case 009 requires bug category and asserts block_auto_promotion.blocked = true. Numbering starts at 009 because 008-split-bundled-issues already exists on main.

Migration: the schema prefers block_auto_promotion over requires_workflow_changes. All components (schema, prompt, post-script) should still be deployed and rolled back together per the lockstep release model. The deprecated boolean is accepted for one release to cover version skew.

Closes fullsend-ai/fullsend#2207

Test plan

  • Post-triage tests pass (including injection sanitization, empty-reason fallback, unmatched-fence remainder, blocked-feature skip-warning absence)
  • Schema validation tests pass (including conditional requirement for auto-promotable categories and deprecated requires_workflow_changes)
  • Both eval fixture repos run (pytest + server endpoints)
  • Eval run confirms case 009 gets triaged and 010 gets ready-to-code
  • Eval lint passes

@rh-hemartin
rh-hemartin requested a review from a team as a code owner August 4, 2026 13:04
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:05 PM UTC · Completed 1:23 PM UTC
Commit: b672253 · View workflow run →

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add effort-estimation gating and block_auto_promotion to triage auto-promotion

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add an effort-estimation skill to score bug/docs/perf effort and gate auto-promotion.
• Replace requires_workflow_changes with triage_summary.block_auto_promotion {blocked, reason}.
• Update post-triage labeling/comment behavior and add schema + post-triage test coverage.
Diagram

graph TD
  A[/"GitHub Issue"/] --> B(["Triage agent"])
  B --> C[["effort-estimation skill"]] --> D[("triage-result schema")]
  B --> E["triage result JSON"] --> F(["post-triage.sh"])
  F --> G{"blocked?"} --> H["Apply triaged + append reason"] --> I(["GitHub labels/comment"])
  G --> J["Apply ready-to-code"] --> I
  K[["post-triage & schema tests"]] --> F
  L[["eval cases 008/009 fixtures"]] --> B
  subgraph Legend
    direction LR
    _ext[/"External"/] ~~~ _agent(["Agent/script"]) ~~~ _skill[["Skill/doc"]] ~~~ _schema[("Schema")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Add numeric `effort_score` alongside `block_auto_promotion`
  • ➕ Improves observability/trending (can chart scores over time)
  • ➕ Avoids encoding the score inside a free-form reason string
  • ➕ Allows future gating policy changes without changing output shape
  • ➖ Expands schema surface area and requires more backward/forward compatibility handling
  • ➖ May tempt downstream consumers to re-implement gating logic outside the triage agent
2. Keep `requires_workflow_changes` and add a separate `block_auto_promotion`
  • ➕ Less breaking for any downstream tooling that still reads the old boolean
  • ➕ Clear separation between workflow permission limits and effort gating
  • ➖ Duplicates concepts and increases risk of conflicting fields (one blocked, one not)
  • ➖ Post-script logic becomes more complex
3. Policy-only gating in post-triage script (no skill)
  • ➕ Centralizes gating rules in one place
  • ➕ No new skill to maintain
  • ➖ Post-script has less context than the model+repo inspection
  • ➖ Harder to justify/communicate decisions in the triage comment without a model-derived rationale

Recommendation: The PR’s approach (a single generalized block_auto_promotion {blocked, reason} gate populated by an effort-estimation skill and reused for workflow-permission blocking) is the best default: it unifies all “don’t auto-dispatch yet” reasons into one contract that the post-script can apply consistently, and it ensures maintainers get an explicit rationale appended to the triage comment. If you want better long-term metrics, consider a follow-up to add an optional numeric effort_score field (with strict schema) rather than parsing it from the reason string.

Files changed (30) +1465 / -49

Enhancement (3) +118 / -19
triage.mdAdd effort-estimation skill and switch gating field to block_auto_promotion +14/-3

Add effort-estimation skill and switch gating field to block_auto_promotion

• Registers the new 'effort-estimation' skill for the triage agent and updates triage guidance to use 'triage_summary.block_auto_promotion' instead of 'requires_workflow_changes'. Adds instructions to run effort estimation for bug/documentation/performance categories and documents when/why to block auto-promotion.

agents/triage.md

post-triage.shGate ready-to-code promotion on block_auto_promotion and append reason to comment +21/-16

Gate ready-to-code promotion on block_auto_promotion and append reason to comment

• Updates post-triage logic to read 'triage_summary.block_auto_promotion.blocked/reason' and, for bug/docs/perf categories, apply 'triaged' (and emit a warning) instead of deferring 'ready-to-code' when blocked. When blocked with a reason, appends a footer to the posted triage comment.

scripts/post-triage.sh

SKILL.mdIntroduce effort-estimation skill spec and scoring rubric +83/-0

Introduce effort-estimation skill spec and scoring rubric

• Adds a new built-in skill document defining how to score effort across scope/testing/domain knowledge/risk, compute an overall score, and map it to 'block_auto_promotion' output. Includes example payloads and guidance to write the reason for maintainers.

skills/effort-estimation/SKILL.md

Tests (23) +1307 / -22
annotations.yamlAdd eval assertions for high-effort multi-component bug gating +56/-0

Add eval assertions for high-effort multi-component bug gating

• Introduces evaluation expectations requiring 'triaged' (and forbidding 'ready-to-code') for a multi-component session/auth/rate-limit bug. Encodes why the scenario should score effort >= 4 and validates that auto-promotion is blocked with an appropriate reason.

eval/triage/cases/008-effort-high-multi-component/annotations.yaml

input.yamlAdd high-effort issue fixture describing session leak + logout invalidation bug +41/-0

Add high-effort issue fixture describing session leak + logout invalidation bug

• Adds a GitHub-issue fixture describing a cross-cutting session memory leak and logout security flaw, including references to impacted components and reproduction/impact details. Designed to drive the triage agent toward a high effort score and auto-promotion block.

eval/triage/cases/008-effort-high-multi-component/input.yaml

README.mdAdd runnable fixture repo README for multi-component auth service +31/-0

Add runnable fixture repo README for multi-component auth service

• Provides a minimal architecture/run guide for the eval repo used in case 008, including environment variables and component layout to support reproducible triage verification.

eval/triage/cases/008-effort-high-multi-component/repo/README.md

app.pyAdd minimal HTTP server fixture wiring auth, users API, and rate limiting +110/-0

Add minimal HTTP server fixture wiring auth, users API, and rate limiting

• Implements a simple HTTP server router used by the eval case to exercise login/logout/session endpoints and user CRUD endpoints, with rate limiting applied per request.

eval/triage/cases/008-effort-high-multi-component/repo/app.py

users.pyAdd authenticated user CRUD handlers for eval repo +65/-0

Add authenticated user CRUD handlers for eval repo

• Defines authenticated endpoint handlers for listing, fetching, updating, and deleting users. Uses the auth middleware decorator and in-memory DB layer to create multi-module touch points for the effort-estimation scenario.

eval/triage/cases/008-effort-high-multi-component/repo/src/api/users.py

session.pyAdd in-memory session store with lazy TTL eviction (intentional leak surface) +77/-0

Add in-memory session store with lazy TTL eviction (intentional leak surface)

• Implements session creation/lookup/refresh on a global dict with TTL expiration checked only on lookup, explicitly documenting that expired sessions remain until accessed. This forms the core of the case’s memory leak and risk analysis surface.

eval/triage/cases/008-effort-high-multi-component/repo/src/auth/session.py

validators.pyAdd email/password validation helpers for eval repo +39/-0

Add email/password validation helpers for eval repo

• Introduces basic email and password validators that raise typed exceptions, supporting the login flow and providing realistic code for triage inspection.

eval/triage/cases/008-effort-high-multi-component/repo/src/auth/validators.py

views.pyAdd login/logout/session views with intentional logout invalidation bug +60/-0

Add login/logout/session views with intentional logout invalidation bug

• Implements login and session status endpoints and includes an intentionally incomplete logout handler that does not invalidate sessions. This drives the ‘multi-component’ and ‘security risk’ aspects of the high-effort triage case.

eval/triage/cases/008-effort-high-multi-component/repo/src/auth/views.py

users.pyAdd in-memory user DB layer for eval repo +75/-0

Add in-memory user DB layer for eval repo

• Provides a small in-memory user store and CRUD-like operations to support the API module and enable cross-package interactions for triage analysis.

eval/triage/cases/008-effort-high-multi-component/repo/src/db/users.py

auth.pyAdd auth middleware decorator using session token validation +36/-0

Add auth middleware decorator using session token validation

• Implements an authentication decorator that validates Bearer tokens via the session store and refreshes session activity. Contributes to the cross-cutting set of modules implicated by the case 008 bug report.

eval/triage/cases/008-effort-high-multi-component/repo/src/middleware/auth.py

rate_limit.pyAdd rate limiter with lazy cleanup note mirroring session behavior +57/-0

Add rate limiter with lazy cleanup note mirroring session behavior

• Implements an in-memory per-IP rate limiter and documents that entries persist for inactive IPs, paralleling the session store’s lazy eviction pattern. This supports the case’s claim that a similar accumulation issue exists in rate limiting.

eval/triage/cases/008-effort-high-multi-component/repo/src/middleware/rate_limit.py

test_db.pyAdd unit tests for in-memory users DB fixture +64/-0

Add unit tests for in-memory users DB fixture

• Adds pytest coverage for user DB functions (get/list/update/delete) to keep the fixture repo runnable and credible for triage inspection.

eval/triage/cases/008-effort-high-multi-component/repo/tests/test_db.py

test_validators.pyAdd validator unit tests for auth fixture +44/-0

Add validator unit tests for auth fixture

• Adds pytest coverage for email/password validators, providing baseline tests while still leaving the targeted session lifecycle gap that the case highlights.

eval/triage/cases/008-effort-high-multi-component/repo/tests/test_validators.py

annotations.yamlAdd eval assertions for low-effort single-file bug auto-promotion +55/-0

Add eval assertions for low-effort single-file bug auto-promotion

• Introduces evaluation expectations requiring 'ready-to-code' (and forbidding 'triaged') for a trivial username-regex bug. Encodes why effort should be < 4 and what verification the triage should perform.

eval/triage/cases/009-effort-low-single-file/annotations.yaml

input.yamlAdd low-effort issue fixture for two-character username rejection +34/-0

Add low-effort issue fixture for two-character username rejection

• Adds a GitHub-issue fixture describing a minimal regex constraint bug with a clear suggested one-character fix. Designed to drive the triage agent toward allowing auto-promotion.

eval/triage/cases/009-effort-low-single-file/input.yaml

README.mdAdd runnable fixture repo README for low-effort auth service +25/-0

Add runnable fixture repo README for low-effort auth service

• Provides basic run/test instructions and endpoint list for the eval repo used in case 009.

eval/triage/cases/009-effort-low-single-file/repo/README.md

app.pyAdd minimal HTTP server fixture for login/logout/register endpoints +58/-0

Add minimal HTTP server fixture for login/logout/register endpoints

• Implements a simple HTTP server router to make the eval repo runnable and to provide realistic context around input validation behavior.

eval/triage/cases/009-effort-low-single-file/repo/app.py

validators.pyAdd validators with username regex that enforces 3–30 chars (bug surface) +57/-0

Add validators with username regex that enforces 3–30 chars (bug surface)

• Defines email/password/username validators and sets 'USERNAME_PATTERN' to require a 3-character minimum via '{2,29}' after the initial letter. The mismatch with the expected 2-character minimum is the intentional bug for case 009.

eval/triage/cases/009-effort-low-single-file/repo/src/auth/validators.py

views.pyAdd login/logout/register views using validators +61/-0

Add login/logout/register views using validators

• Implements handler logic that invokes the validators (including username validation during registration), providing the call path that makes the regex issue user-visible.

eval/triage/cases/009-effort-low-single-file/repo/src/auth/views.py

test_validators.pyAdd validator tests including 3-char username boundary (intended to be updated by fix) +105/-0

Add validator tests including 3-char username boundary (intended to be updated by fix)

• Adds pytest coverage for email/password/username validation, including boundaries at 3 and 30 characters and a failing-too-short case at 2 chars. Serves as existing coverage that makes the bug low-effort to fix.

eval/triage/cases/009-effort-low-single-file/repo/tests/test_validators.py

test_views.pyAdd view handler unit tests for login and registration flows +88/-0

Add view handler unit tests for login and registration flows

• Adds tests for missing-field behavior and basic valid/invalid registration logic, ensuring the fixture repo is runnable and that validation errors surface through handlers.

eval/triage/cases/009-effort-low-single-file/repo/tests/test_views.py

post-triage-test.shExpand post-triage automation tests for block_auto_promotion gating +47/-22

Expand post-triage automation tests for block_auto_promotion gating

• Replaces workflow-change-only tests with broader auto-promotion blocking coverage: blocked vs unblocked behavior, label application differences, warning output, and verifying that the blocked reason footer is appended for bug/docs/perf but not for feature issues.

scripts/post-triage-test.sh

validate-output-schema-test.shAdd schema validation tests for block_auto_promotion required fields/shape +22/-0

Add schema validation tests for block_auto_promotion required fields/shape

• Adds positive and negative schema tests ensuring 'block_auto_promotion' requires both 'blocked' and 'reason' and rejects unknown extra fields.

scripts/validate-output-schema-test.sh

Documentation (2) +30 / -7
code.mdUpdate ready-to-code label semantics to reflect auto-promotion blocking +1/-1

Update ready-to-code label semantics to reflect auto-promotion blocking

• Updates the 'ready-to-code' label description to reflect that it is applied by the triage post-script when auto-promotion is not blocked, and is otherwise applied manually after human review (e.g., high-effort issues or workflow changes).

docs/code.md

triage.mdDocument effort-estimation skill and block_auto_promotion-driven triage behavior +29/-6

Document effort-estimation skill and block_auto_promotion-driven triage behavior

• Updates label meaning descriptions to reflect that bugs/docs can be either 'ready-to-code' or 'triaged' depending on 'block_auto_promotion'. Adds a new section documenting the 'effort-estimation' skill, its scoring dimensions/threshold, and how repositories can override it with a local skill implementation.

docs/triage.md

Other (2) +10 / -1
triage.yamlRegister effort-estimation skill in triage harness configuration +1/-0

Register effort-estimation skill in triage harness configuration

• Adds 'skills/effort-estimation' to the triage harness so the skill is available during triage runs and evaluations.

harness/triage.yaml

triage-result.schema.jsonReplace requires_workflow_changes with block_auto_promotion object in schema +9/-1

Replace requires_workflow_changes with block_auto_promotion object in schema

• Updates the triage result JSON schema to require 'triage_summary.block_auto_promotion' (when present) to include both 'blocked' and 'reason', and disallows additional properties. Removes the legacy 'requires_workflow_changes' boolean from the schema.

schemas/triage-result.schema.json

@qodo-code-review

qodo-code-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. BLOCK_REASON unsanitized in ::warning:: ✓ Resolved 📜 Skill insight ⛨ Security
Description
scripts/post-triage.sh interpolates the agent-produced/untrusted BLOCK_REASON directly into a
GitHub Actions ::warning:: workflow command, allowing workflow-command injection via ::,
newlines, or control characters that can create additional annotations or disrupt runner command
parsing. This violates PR Compliance ID 1538382’s requirement to sanitize every interpolated
workflow-command value individually.
Code

scripts/post-triage.sh[398]

+      echo "::warning::Skipping ready-to-code — auto-promotion blocked: ${BLOCK_REASON}"
Relevance

●●● Strong

Strong recent precedent: sanitize untrusted values before embedding in GitHub Actions workflow
commands to prevent injection.

PR-#573

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538382 requires that all variables interpolated into GitHub Actions workflow
commands be sanitized individually, yet the script reads block_auto_promotion.reason from the
triage/agent result JSON into BLOCK_REASON and then embeds it directly into an `echo
"::warning::...${BLOCK_REASON}" line with no escaping. Because ::warning::` lines are parsed
specially by GitHub Actions runners, any unescaped newlines, :: sequences, or other
special/control characters in BLOCK_REASON can be interpreted as additional workflow
commands/annotations or otherwise corrupt workflow-command parsing.

scripts/post-triage.sh[392-399]
scripts/post-triage.sh[382-407]
harness/triage.yaml[24-29]
Skill: pr-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
`scripts/post-triage.sh` emits a GitHub Actions workflow command (`::warning::...`) that interpolates `BLOCK_REASON` without sanitization. Because `BLOCK_REASON` originates from the triage agent output (and can be influenced by issue content), it must be sanitized (or removed from the workflow command) to prevent GitHub Actions workflow-command/log injection.

## Issue Context
- `BLOCK_REASON` is extracted from the triage result JSON (agent-produced/untrusted text).
- The value is embedded in an `::warning::` line, which is a special GitHub Actions workflow-command channel; untrusted content inside `::warning::...` can be interpreted as additional workflow commands if it contains `::`, `%0A/%0D`, raw `\n/\r`, control characters, or ANSI escape sequences.
- This is a workflow-command/log injection risk (not shell execution) and violates PR Compliance ID 1538382’s requirement that each interpolated workflow-command value be sanitized individually.
- Acceptable approaches include either not including the reason inside the workflow command (emit it as a separate plain log line), or escaping the value for GitHub Actions command format (at minimum `%` -> `%25`, `\r` -> `%0D`, `\n` -> `%0A`) before interpolation.
- Optionally add a regression test in `scripts/post-triage-test.sh` using a reason containing a newline and a `::warning::`-like prefix to ensure it cannot produce a second workflow command.

## Fix Focus Areas
- scripts/post-triage.sh[392-399]

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


2. Protected paths modified in PR 📜 Skill insight § Compliance
Description
This PR modifies protected governance/infrastructure paths (e.g., agents/, scripts/, harness/,
schemas/, skills/), which must not be auto-approved and requires human review. Even with the
linked issue (#2207), a protected-path finding is required.
Code

skills/effort-estimation/SKILL.md[R1-4]

+---
+name: effort-estimation
+description: >-
+  Score implementation effort for triaged issues and decide whether to block
Relevance

●● Moderate

Protected-path governance concerns appear in reviews, but precedent is mixed on how
strictly/explicitly it’s enforced.

PR-#631
PR-#476

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538392 requires raising a finding whenever protected paths are modified. The PR
adds/changes files under protected directories including skills/ and scripts/ (and others), so
this PR must receive human approval and cannot be auto-approved.

skills/effort-estimation/SKILL.md[1-7]
scripts/post-triage.sh[387-406]
Skill: pr-review



Informational

3. Missing block reason silent ✓ Resolved 🐞 Bug ☼ Reliability
Description
If block_auto_promotion.blocked is true but block_auto_promotion.reason is missing/empty,
post-triage.sh still applies triaged but adds no explanation because it only appends the footer
when BLOCK_REASON is non-empty. The script explicitly warns it can’t fully trust schema validation,
so this can occur in best-effort outputs and leaves maintainers without the blocking rationale.
Code

scripts/post-triage.sh[393]

+    BLOCK_REASON=$(jq -r '.triage_summary.block_auto_promotion.reason // empty' "${RESULT_FILE}")
Relevance

●●● Strong

Deterministic reliability fix; repo has precedent for best-effort jq parsing when fields may be
missing/null.

PR-#284

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The script extracts reason with // empty and only appends the explanatory footer when it is
non-empty; separately, the script documents that validation may not have gated malformed output,
making this path plausible.

scripts/post-triage.sh[271-276]
scripts/post-triage.sh[392-407]

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

### Issue description
When auto-promotion is blocked, `scripts/post-triage.sh` may end up with an empty `${BLOCK_REASON}` (e.g., best-effort agent output after failed validations). In that case the script blocks promotion but does not append any explanation to the issue comment.

### Issue Context
The script notes it cannot fully trust that schema validation prevented malformed output, so defensive handling should produce a human-readable reason even when missing.

### Fix Focus Areas
- scripts/post-triage.sh[271-276]
- scripts/post-triage.sh[392-407]

### Suggested fix
When `${BLOCKED} == true` for bug/documentation/performance:
- If `${BLOCK_REASON}` is empty, set a fallback reason (e.g., `"Auto-promotion blocked, but triage did not provide a reason."`).
- Append the fallback to the comment (and optionally emit a separate plain-log warning).

Optionally add a `scripts/post-triage-test.sh` case where `blocked: true` but `reason` is missing/empty and assert the comment contains the fallback footer.

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


Grey Divider

Context sources
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

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.sh Outdated
Comment thread skills/effort-estimation/SKILL.md
Comment thread scripts/post-triage.sh
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [protected-path] agents/triage.md, harness/triage.yaml, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh, skills/effort-estimation/SKILL.md — This PR modifies 7 files under protected governance/infrastructure paths (agents/, harness/, scripts/, skills/). The PR links to dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207 and the description explains the rationale for the changes. Human approval is always required for protected-path changes regardless of context.

  • [backward-compatibility] scripts/post-triage.src.sh, schemas/triage-result.schema.json — The schema retains requires_workflow_changes as a deprecated optional field ("Kept optional for one release so lockstep-skewed agent output still validates"), but the post-script no longer reads it — only block_auto_promotion.blocked is consumed. During a lockstep-skew window, old agent output emitting requires_workflow_changes: true without block_auto_promotion would silently lose the block intent. The practical risk is mitigated: for auto-promoting categories (bug/documentation/performance), the allOf conditional requires block_auto_promotion, so schema validation fails closed; for non-auto-promoting categories, the block intent has no behavioral effect. Consider OR-ing both fields during the deprecation window for completeness.

Low

  • [scope-creep] scripts/post-triage.sh, agents/triage.md — The PR adds fenced-code-block stripping from triage comments and changes the agent prompt from "include the proposed test case as a fenced code block" to "do not include fenced code blocks." This behavioral change is not scoped to the linked issue #2207 (effort-based gating).

  • [injection-defense] scripts/post-triage.src.shBLOCK_REASON extracted from untrusted agent JSON undergoes :: collapsing but does not pass through _gha_sanitize(). Not exploitable: BLOCK_REASON is never interpolated into a GHA workflow command — the ::warning:: uses a fixed string. Defense-in-depth observation only.

  • [schema-contract] schemas/triage-result.schema.json — The block_auto_promotion.reason field has minLength: 1, yet the post-script includes a fallback for empty reasons. The test blocked-empty-reason-gets-fallback validates a code path that schema-conforming input can never reach. This is defense-in-depth, not a bug.

Previous run

Review

Findings

Medium

  • [protected-path] agents/triage.md, harness/triage.yaml, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh, skills/effort-estimation/SKILL.md — This PR modifies 7 files under protected governance/infrastructure paths (agents/, harness/, scripts/, skills/). The PR links to dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207 and the description explains the rationale for the changes. Human approval is always required for protected-path changes regardless of context.

  • [backward-compatibility] schemas/triage-result.schema.json:200 — The requires_workflow_changes property is removed and replaced with block_auto_promotion. Because triage_summary uses additionalProperties: false, any agent output still emitting the old field will fail schema validation. The conditional allOf also makes block_auto_promotion required for bug/documentation/performance categories. This is an intentional breaking change; the PR body correctly identifies the lockstep deployment requirement. No mechanical enforcement exists to prevent a mismatched deployment (e.g., new schema with old prompt).

  • [edge-case] scripts/post-triage.sh:932 — The fenced code block stripping uses sed '/^\x60\x60\x60/,/^\x60\x60\x60/d' which has an edge case: an odd number of line-start triple-backtick markers causes sed to delete from the unmatched opener through EOF, destroying the remainder of the comment. Additionally, the grep -q guard uses an unanchored pattern (matches triple backticks anywhere in a line) while sed is anchored to line-start (^), causing false-positive warnings for inline backtick sequences. The same pattern appears in post-triage.src.sh.

  • [naming-collision] eval/triage/cases/008-effort-high-multi-component/annotations.yaml — The base branch already contains eval/triage/cases/008-split-bundled-issues/. This PR adds a second 008- prefixed directory, breaking the sequential numbering convention (001–007 are unique). After merge, two directories share the 008- prefix.

Low

  • [scope-creep] scripts/post-triage.sh:932, agents/triage.md:370 — The PR adds fenced-code-block stripping from triage comments and changes the agent prompt from "include the proposed test case as a fenced code block" to "do not include fenced code blocks." This behavioral change is not scoped to the linked issue #2207 (which concerns effort-based gating).

  • [test-inadequate] scripts/post-triage-test.sh — No test verifies that when BLOCKED=true and AUTO_CODE_ALLOWED=false (e.g., feature category), the ::warning::Skipping ready-to-code message is NOT emitted. The existing blocked-feature-no-block-reason-in-comment test checks the comment body but not the CI warning annotation.

  • [scope-creep] harness/triage.yaml:26 — The effort-estimation skill appears in both agents/triage.md (agent-level skills) and harness/triage.yaml (harness-level skills). This matches the existing issue-labels pattern and mergeSkills deduplicates by basename, so both entries are harmless.

  • [structural-consistency] eval/triage/cases/009-effort-low-single-file/annotations.yaml — Case 008 annotations include a triage_summary block (asserting category: bug and block_auto_promotion.blocked: true) while case 009 omits it entirely. Both test the effort-estimation feature.

  • [schema-contract] schemas/triage-result.schema.json:197 — The block_auto_promotion.reason field has minLength: 1 (empty strings rejected by schema), yet the post-script includes a fallback for empty reasons. The test blocked-empty-reason-gets-fallback validates a code path that schema-conforming input can never reach. This is defense-in-depth, not a bug.

Previous run (2)

Review

Findings

Medium

  • [removal-staleness] FEATURES.md:20 — FEATURES.md references requires_workflow_changes as the canonical example of an agent+scripts+schema feature, but this PR renames the field to block_auto_promotion. FEATURES.md is not in the PR diff, so this reference is now stale.
    Remediation: Update the example on line 20 of FEATURES.md to reference block_auto_promotion instead of requires_workflow_changes.

  • [protected-path] agents/triage.md, harness/triage.yaml, scripts/post-triage.sh, scripts/post-triage.src.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh, skills/effort-estimation/SKILL.md — This PR modifies 7 files under protected paths (agents/, harness/, scripts/, skills/). The linked issue (dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207) and PR description explain the rationale for the changes. Human approval is required for all protected-path changes regardless of justification.

Low

  • [logic-error] scripts/post-triage.sh:510 — The fenced code block stripping uses sed '/^```/d' which only deletes fence delimiter lines but preserves content between them. After stripping, raw code appears as unformatted prose in the posted comment. This is defense-in-depth (the agent prompt now prohibits fenced code blocks), and the leaked content is cosmetic rather than functional, but the stripping does not match its warning message.
    Remediation: Replace sed '/^```/d' with sed '/^```/,/^```/d' to strip full fenced code blocks, or rename the warning to accurately describe the behavior.

  • [scope-creep] agents/triage.md — The instruction discouraging fenced code blocks in triage comments is unrelated to the effort-estimation feature authorized by issue #2207. Appears to be a deliberate design decision with corresponding enforcement in post-triage.sh.

  • [schema-migration] schemas/triage-result.schema.json — Removing requires_workflow_changes and adding block_auto_promotion is a breaking schema change. All three components (schema, prompt, post-script) are updated atomically in this PR, and the PR body documents the lockstep deployment requirement per AGENTS.md section 6.

  • [punctuation-consistency] skills/effort-estimation/SKILL.md:22 — The range notation 1--5 uses a double-hyphen, inconsistent with the en-dash range convention used across other skill and agent files.

  • [test-adequacy] scripts/post-triage-test.sh — The blocked-empty-reason-gets-fallback test uses "reason":"" which would fail schema validation (minLength: 1). The test validates the post-script's defense-in-depth fallback. Schema-level validation is separately covered by block-auto-promotion-empty-reason-rejected in validate-output-schema-test.sh.

  • [edge-case] scripts/post-triage.sh — The block_auto_promotion gate silently ignores the block for feature categories. This is correct behavior (features already route to triaged) and is tested explicitly.

Previous run (3)

Review

Findings

Medium

  • [logic-error] scripts/post-triage.sh:64 — The fenced code block stripping uses sed '/^```/d' which only deletes the fence delimiter lines (e.g., ```bash and ```) but preserves all code content lines between them. After stripping, raw code appears as unformatted prose in the posted GitHub comment. The test comment claims this "mirrors post-scribe.sh enforcement," but post-scribe.sh uses gate_reject to reject the entire item when code blocks are detected — it does not attempt partial stripping.
    Remediation: Replace sed '/^```/d' with sed '/^```/,/^```/d' to strip the full fenced code block including its content, or update the warning message to accurately describe the behavior.

  • [protected-path] agents/triage.md, harness/triage.yaml, scripts/post-triage.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh, skills/effort-estimation/SKILL.md — This PR modifies 6 files under protected paths (agents/, harness/, scripts/, skills/). The linked issue (dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207) and PR description explain the rationale for the changes. Human approval is required for all protected-path changes regardless of justification.

Low

  • [injection-vuln] scripts/post-triage.sh:401 — The prior finding (triple-colon bypass of :: sanitization) is now fixed. The new while loop iterates until no :: sequences remain, correctly collapsing :::error::: to :error:. BLOCK_REASON never reaches the GHA log — it is only embedded in COMMENT via printf '%s'. Residual risk is negligible.

  • [scope-creep] agents/triage.md — The instruction discouraging fenced code blocks in triage comments is unrelated to the effort-estimation feature authorized by issue #2207.

  • [schema-migration] schemas/triage-result.schema.json:155 — Removing requires_workflow_changes and adding block_auto_promotion is a breaking schema change. All three components (schema, prompt, post-script) are updated atomically in this PR, and the PR body documents the lockstep deployment requirement per AGENTS.md section 6.

  • [punctuation-consistency] agents/triage.md:157 — The diff changes em-dash (—) to double-hyphen (--) in one location, inconsistent with the em-dash style used elsewhere in the same file.

  • [punctuation-consistency] skills/effort-estimation/SKILL.md:22 — The range notation 1--5 uses a double-hyphen, inconsistent with the single-hyphen range convention used across all other skill and agent files.

Previous run (4)

Review

Findings

Medium

  • [protected-path] agents/triage.md, harness/triage.yaml, scripts/post-triage.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh, skills/effort-estimation/SKILL.md — This PR modifies 6 files under protected paths (agents/, harness/, scripts/, skills/). The linked issue (dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207) and PR description explain the rationale for the changes. Human approval is required for all protected-path changes regardless of justification.

Low

  • [API-contract-contradiction] schemas/triage-result.schema.json:156 — The schema description says "Only include when blocking; omit entirely when effort is low enough for auto-promotion" but the effort-estimation SKILL.md and triage.md both instruct the agent to emit block_auto_promotion even with blocked: false. Runtime behavior is unaffected since the post-script handles both cases via jq // false, but the contradictory guidance may cause inconsistent agent output.

  • [injection-vuln] scripts/post-triage.sh:395 — The :: collapsing sanitization (${BLOCK_REASON//::/:}) is bypassable with triple colons: :::error::: becomes ::error:: after substitution. Not currently exploitable because BLOCK_REASON is never echoed to the GHA log — it is only embedded in COMMENT which is piped to tools via printf.
    Remediation: Consider collapsing runs of 2+ colons to a single colon via sed 's/::*/:/g'.

  • [scope-creep] agents/triage.md — The instruction discouraging fenced code blocks in triage comments is unrelated to the effort-estimation feature authorized by issue #2207.

  • [unauthorized-refactoring] schemas/triage-result.schema.json:155 — The PR replaces requires_workflow_changes with block_auto_promotion, unifying workflow-change detection (Triage should detect likely GHA workflow modifications and skip automatic code agent #325) and effort estimation (#2207) under one structure. The unification is documented in the PR body and is a reasonable engineering choice, but extends beyond the issue's explicit authorization.

  • [schema-migration] schemas/triage-result.schema.json:155 — Removing requires_workflow_changes and adding block_auto_promotion is a breaking schema change. All three components (schema, prompt, post-script) are updated atomically in this PR, and the PR body documents the lockstep deployment requirement per AGENTS.md section 6.

  • [punctuation-consistency] agents/triage.md — The diff changes em-dash (—) to double-hyphen (--) in one location, inconsistent with the em-dash style used elsewhere in the same file.

  • [test-adequacy] scripts/validate-output-schema-test.sh — No schema validation test covers block_auto_promotion with an empty reason string ("reason": ""). The schema enforces minLength: 1 on reason; adding a test (expecting rejection) would confirm the constraint works.

Previous run (5)

Review

Findings

High

  • [GHA workflow command injection] scripts/post-triage.sh:398BLOCK_REASON is extracted from untrusted agent JSON via jq -r and interpolated directly into a GHA workflow command: echo "::warning::Skipping ready-to-code — auto-promotion blocked: ${BLOCK_REASON}". The jq -r flag decodes JSON escape sequences into literal characters, so a reason containing \n::stop-commands::token or \n::error::injected would produce multiple output lines that GitHub Actions interprets as separate workflow commands. This is the first unconstrained free-text string in this script that is interpolated into a workflow command — all prior uses (LA_LABEL, TARGET_REPO) are either character-validated or schema-constrained.
    Remediation: Sanitize BLOCK_REASON before interpolating into the ::warning:: command. Either strip newlines (BLOCK_REASON_SAFE=$(printf '%s' "${BLOCK_REASON}" | tr '\n' ' ' | sed 's/%/%25/g')) or use a fixed message without interpolation: echo "::warning::Skipping ready-to-code — auto-promotion blocked (see comment for details)".

Medium

  • [backward-compatibility] schemas/triage-result.schema.json:155 — Removing requires_workflow_changes from triage_summary (which has additionalProperties: false) means any existing triage agent output containing this field will fail schema validation. The lockstep versioning model (AGENTS.md section 6) mitigates this for normal deployments since the schema, agent prompt, and post-script deploy atomically. However, version skew during rollback or partial deployment would cause fail-open behavior: old agent results with requires_workflow_changes would pass through without blocking auto-promotion.
    Remediation: Verify all components are deployed atomically in the same release tag. Consider documenting the migration in the PR description for operators who may need to roll back.

  • [edge-case] schemas/triage-result.schema.json:155 — The block_auto_promotion object requires both blocked and reason (with minLength: 1) whenever present, but the object itself is optional on triage_summary. This means blocked: false + reason is a valid but practically unreachable state — the skill would simply omit the field when not blocking rather than emit blocked: false with a forced reason. The blocked boolean becomes redundant (presence of the object implies blocking).
    Remediation: Either (a) make reason required only when blocked: true via a conditional schema (if/then), or (b) document the convention that the field should only be present when blocking.

  • [protected-path] agents/triage.md, harness/triage.yaml, scripts/post-triage.sh, scripts/post-triage-test.sh, scripts/validate-output-schema-test.sh, skills/effort-estimation/SKILL.md — This PR modifies 6 files under protected paths (agents/, harness/, scripts/, skills/). The linked issue (dispatch: no effort-based gating before auto-promoting issues to coder fullsend#2207) and PR description provide context for the changes. Human approval is required for all protected-path changes regardless of justification.

Low

  • [test-adequacy] scripts/post-triage-test.sh — No test covers block_auto_promotion present with blocked: false and a reason string. While blocked: false is functionally equivalent to omitting the field (both fall through via jq // false), a test for this state would guard against regressions in the bash string comparison logic.

  • [schema-validation-gap] schemas/triage-result.schema.json:155 — The reason field has minLength: 1 but no maxLength. The top-level comment field has maxLength: 16384 for defense-in-depth. Adding a similar bound (e.g., maxLength: 1024) to reason would prevent unnecessarily large ::warning:: annotations and maintain consistency.


Labels: PR modifies triage agent instructions, post-script, skill, and eval cases.


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.

@rh-hemartin rh-hemartin self-assigned this Aug 4, 2026
@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from b672253 to 3d72bb1 Compare August 4, 2026 14:17
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 2:18 PM UTC · Ended 2:31 PM UTC
Commit: 3d72bb1 · View workflow run →

@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from 3d72bb1 to 2686001 Compare August 4, 2026 14:30
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 2:32 PM UTC · Ended 2:45 PM UTC
Commit: 2686001 · View workflow run →

@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from 2686001 to 743fabd Compare August 4, 2026 14:44
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 2:45 PM UTC · Ended 2:59 PM UTC
Commit: 743fabd · View workflow run →

@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from 743fabd to f8405d6 Compare August 4, 2026 14:58
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:00 PM UTC · Completed 3:16 PM UTC
Commit: f8405d6 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 4, 2026 15:16

Superseded by updated review

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 4, 2026

@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.

[MEDIUM] Effort-estimation threshold and traceability are unverified before merge (PR description / skills/effort-estimation/SKILL.md)

Three compounding gaps:

  1. The PR body says "Closes #2207", but repos/fullsend-ai/agents/issues/2207 returns 404 — issue #2207 does not exist in this repo. The actual issue is fullsend-ai/fullsend#2207 ("dispatch: no effort-based gating before auto-promoting issues to coder"), which is open. GitHub's auto-close syntax only closes same-repo issues unless the owner/repo#number form is used, so merging as-is will not close the tracked issue, and the entire feature (the four scoring dimensions, 1-5 scale, and the specific ">=4 blocks" threshold) is only traceable to a cross-repo issue that the PR text doesn't correctly reference.

  2. There is no cited derivation (historical data, prior incidents) for the ">=4 average blocks" threshold in skills/effort-estimation/SKILL.md; it reads as a picked-because-it-feels-right number, and no eval case near the boundary (e.g. ~3.5-4.5) exists to confirm the cutoff behaves as intended — the two added fixtures (eval/triage/cases/008, 009) are deliberately constructed extremes.

  3. The PR's own test plan leaves unchecked: "Eval run confirms case 008 gets triaged and 009 gets ready-to-code" — meaning the one piece of evidence that would validate the threshold against a real triage agent run has not been collected, and this is shipped as final production gating logic rather than as an explicitly-flagged, tunable heuristic pending that verification.

Suggestion: update the PR description to "Closes fullsend-ai/fullsend#2207" so the cross-repo issue actually closes on merge. Before merging the gating logic as final, run the eval harness on cases 008/009 (and ideally a boundary case around effort ~4.0) and record actual pass/fail, or explicitly mark the threshold as a tunable default subject to recalibration pending that evidence.

Comment thread docs/triage.md Outdated
Comment thread agents/triage.md
@rh-hemartin

rh-hemartin commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

About the compounding gaps:

Fixed the PR body, the decision of using 1 to 5 has been raised in internal communications, not sure there is a reference and I don't think we need a middle case, it will bring flakyness, let's see how this goes; and the test plan has been checked.

@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from f8405d6 to 6423d80 Compare August 5, 2026 06:31
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 5, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:32 AM UTC · Completed 6:51 AM UTC
Commit: 6423d80 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@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.

Review-only pass (inline comments added below). One additional finding on unchanged code that isn't part of this PR's diff, so noting it here instead of inline:

[HIGH] Auto-promotion gate is undermined by the unconditional /fs-code footer appended to the same comment (scripts/post-triage.sh, the "Append action-hints footer (sufficient only)" block, ~line 558)

When block_auto_promotion.blocked is true and category is bug/documentation/performance, the script appends "Auto-promotion blocked: <reason>". But the unconditional "Next steps" footer, gated only on ACTION == "sufficient" with no check of AUTO_PROMOTION_BLOCKED, still appends "/fs-code — agent creates a PR to implement this issue". docs/code.md confirms /fs-code triggers the code agent independent of label state ("triggered when the ready-to-code label is applied... or via the /fs-code command"). The result is a single comment that both explains why the issue was held for review and hands the reader a working one-click bypass of that exact hold.

Suggested fix: Suppress or reword the Next-steps footer when AUTO_PROMOTION_BLOCKED is true, e.g. "This issue was held for review — run /fs-code only after confirming the concerns above."

Comment thread schemas/triage-result.schema.json
Comment thread skills/effort-estimation/SKILL.md
Comment thread skills/effort-estimation/SKILL.md
Comment thread eval/triage/cases/008-effort-high-multi-component/annotations.yaml Outdated
Comment thread agents/triage.md
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 17, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:32 AM UTC · Completed 10:49 AM UTC

Commit: 8a42a64 · View workflow run →

@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.

Additional review findings (deduplicated against existing comments/reviews on this PR).

Comment thread skills/effort-estimation/SKILL.md
Comment thread scripts/post-triage.src.sh
Comment thread eval/triage/cases/008-effort-high-multi-component/annotations.yaml Outdated
@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from 8a42a64 to 3d049e8 Compare August 21, 2026 07:18
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 21, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 7:20 AM UTC · Ended 7:32 AM UTC

Commit: 3d049e8 · View workflow run →

@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from 3d049e8 to b6634b3 Compare August 21, 2026 07:31
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 21, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 7:34 AM UTC · Ended 7:37 AM UTC

Commit: b6634b3 · View workflow run →

@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from b6634b3 to dbe7f48 Compare August 21, 2026 07:36
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 21, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:38 AM UTC · Completed 8:00 AM UTC

Commit: dbe7f48 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

Add an effort-estimation skill that scores issues on scope, testing,
domain knowledge, and risk (1-5 each). When overall effort >= 4, or any
single dimension scores 5, auto-promotion to the code agent is blocked
and the issue receives 'triaged' instead of 'ready-to-code'.

Replace the single-purpose requires_workflow_changes boolean with a
general-purpose block_auto_promotion object (blocked + reason) that
OR-combines effort gating, workflow-file detection, and any future
blocking conditions. Keep requires_workflow_changes as a deprecated
optional property for one release so lockstep-skewed agent output still
validates.

Key changes:
- skills/effort-estimation/SKILL.md: new skill with 4-dimension rubric,
  OR-combination cross-reference, and RICE Effort design note
- agents/triage.md: block_auto_promotion instructions with category
  qualification (bug/documentation/performance only)
- schemas/triage-result.schema.json: block_auto_promotion schema with
  conditional requirement for auto-promotable categories, maxLength on
  reason, deprecated requires_workflow_changes
- scripts/post-triage.src.sh: block gate with reason fallback, paired
  line-start fence stripping (unmatched openers preserved), ::
  sanitization, conditional /fs-code footer
- eval/triage/cases/009: high-effort fixture requiring bug category and
  block_auto_promotion.blocked
- eval/triage/cases/010: low-effort fixture confirming auto-promotion
- eval/triage/cases/011: mixed-profile fixture (Risk=5, other dimensions
  low) for the any-dimension-5 rule

Closes fullsend-ai/fullsend#2207

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Hector Martinez <hemartin@redhat.com>
@rh-hemartin
rh-hemartin force-pushed the feat/2207-effort-gating branch from dbe7f48 to 898ec66 Compare August 28, 2026 09:19
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 28, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:21 AM UTC · Completed 9:42 AM UTC

Commit: 898ec66 · View workflow run →

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

@fullsend-ai-review fullsend-ai-review Bot added the risk/elevated PR risk: elevated label Aug 28, 2026
@fullsend-ai-review

Copy link
Copy Markdown

Risk Assessment: elevated (3/5)

Details

Elevated risk driven by large change size (35 files, 1890 lines, large blast radius), high protected-path and security-sensitive file counts, and significant churn/fix history in core triage scripts. Mitigated by non-bot experienced author, no CI/dependency changes, and well-scoped issue with active discussion and meeting approval.

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note: The following review comments could not be posted on the diff (GitHub returned 422) and are included here instead:

  • scripts/post-triage.src.sh (file-level): Line 395 · [medium] backward-compatibility

The schema retains requires_workflow_changes as a deprecated optional field but the post-script no longer reads it. During a lockstep-skew window, old agent output emitting requires_workflow_changes: true without block_auto_promotion would silently lose block intent. Risk is mitigated: for auto-promoting categories, the allOf conditional requires block_auto_promotion (fail-closed); for non-auto-promoting categories, block intent has no behavioral effect.

Suggested fix: OR both fields during the deprecation window: read requires_workflow_changes and set BLOCKED=true if either field indicates blocking.

@rh-hemartin rh-hemartin closed this Sep 1, 2026
@fullsend-ai-retro

fullsend-ai-retro Bot commented Sep 1, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 9:06 AM UTC · Completed 9:25 AM UTC

Commit: 898ec66 · View workflow run →

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

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #646 — effort-estimation skill and auto-promotion gate

Outcome: Closed without merging after ~28 days and 10 force-push iterations.

Timeline

  1. Aug 4 13:04 — PR opened by rh-hemartin. 35 files, ~1800 additions. Adds effort-estimation skill scoring issues 1–5 across scope/testing/domain/risk, replaces requires_workflow_changes with block_auto_promotion object, and updates post-triage gating logic.
  2. Aug 4 13:05–13:23 — First review agent run (30912065420). Found GHA workflow-command injection (HIGH), backward-compat concern (MEDIUM), schema edge-case (MEDIUM), protected paths (MEDIUM), test gap (LOW), schema validation gap (LOW). Verdict: request-changes. Cost: $8.25.
  3. Aug 4 14:17–14:58 — Three rapid force-pushes; three review runs cancelled (superseded).
  4. Aug 4 14:58–15:16 — Second successful review run on updated commit.
  5. Aug 4 19:48 — Human reviewer waynesun09 submits Round 1: 3 findings including wrong skill-override path in docs (HIGH) and missing fenced-code-block enforcement (MEDIUM).
  6. Aug 5 06:25–06:30 — Author addresses Round 1. Adds fence-stripping code, fixes PR body.
  7. Aug 5 13:34 — waynesun09 Round 2: 5 findings including /fs-code footer undermining the block gate (HIGH) and missing conditional schema requirement (HIGH).
  8. Aug 17–21 — Multiple force-push iterations. Author addresses schema conditional requirements, adds explanatory notes, tightens eval fixtures.
  9. Aug 19 17:48 — waynesun09 Round 3: 3 findings including conflicting instruction sources (HIGH) and eval fixture ambiguity (MEDIUM).
  10. Aug 28 09:19–09:43 — Final review agent run. Risk assessment 3/5. Cost: $12.37.
  11. Sep 1 09:04 — PR closed without merging.

What went well

  • Review agent caught a real security issue — the GHA workflow-command injection finding was legitimate and addressed by the author. The security sub-agent consistently performs well on shell-script injection patterns.
  • Human review was exceptionally thorough — 12 substantive findings across 3 rounds, with 4 at HIGH severity. Many findings required deep domain knowledge of the triage pipeline, cross-agent conventions, and schema-feature alignment.
  • Cancelled-run management worked — superseded review dispatches were cancelled promptly, limiting wasted compute.

Review quality gaps

The human reviewer caught several high-impact issues the review agent missed:

  1. Feature self-contradiction: The /fs-code footer was appended unconditionally, providing a one-click bypass of the blocking gate the PR was introducing. The intent-coherence sub-agent checked scope alignment but missed this logical contradiction within the implementation itself.
  2. Wrong documentation path: docs/triage.md documented the override path as skills/effort-estimation/SKILL.md instead of .agents/skills/effort-estimation/SKILL.md. The docs-currency sub-agent reported "no stale documentation" despite the correct convention being visible in the same document section.
  3. Schema completeness gap: block_auto_promotion was entirely optional. Without a conditional requirement for auto-promotable categories, the model could omit the field and auto-promotion would proceed silently — defeating the feature's purpose.
  4. Eval fixture test-path leakage: Eval case 008 could pass trivially if the model classified the issue as security instead of bug, since security issues route to triaged regardless of effort score.

Evidence for existing issues

Proposals filed

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 triage-agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dispatch: no effort-based gating before auto-promoting issues to coder

2 participants