From a1724c5e5deec3dcd3597666a0ae5f99f8077ed1 Mon Sep 17 00:00:00 2001 From: guy oron Date: Mon, 17 Aug 2026 13:58:42 +0300 Subject: [PATCH] feat(dispatch): route /fs-plan-tests to the qualityflow stage Custom agents registered via config.yaml agents[] cannot be triggered by comment in per-org mode: the bash router only knows the built-in commands, and the harness CEL path (harness-dispatch) reads .fullsend/config.yaml from the caller repo, which per-org enrolled repos do not have. A /fs-plan-tests comment therefore routes nowhere and the run exits with "No stage matched". Adds the case branch this file documents as the way to add a stage, plus the qualityflow -> coder role mapping (declared by its harness) and the existing fork gate, since the agent commits generated tests to the PR branch. Interim until harness CEL dispatch becomes primary (#2902), which removes the bash routing this branch lives in. Signed-off-by: guy oron --- .../.github/workflows/dispatch.yml | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml b/internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml index 2f1c39f733..a213adb542 100644 --- a/internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml +++ b/internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml @@ -1,5 +1,5 @@ --- -# lint-workflow-size: max-lines=610 +# lint-workflow-size: max-lines=625 # Dispatcher workflow that routes events to agent workflows based on stage. # Routing logic determines the stage from event context — the shim only # forwards the raw event. Adding a new stage requires only a case branch @@ -163,6 +163,15 @@ jobs: STAGE="prioritize" fi ;; + /fs-plan-tests) + # QualityFlow custom agent. Mirrors its harness CEL trigger + # (change_proposal, non-fork); fork gate is in "Resolve PR + # head" below. Interim until CEL dispatch is primary (#2902). + if [[ "${COMMENT_USER_TYPE}" != "Bot" ]] && is_authorized \ + && [[ "${ISSUE_HAS_PR}" == "true" ]]; then + STAGE="qualityflow" + fi + ;; *) # Intentionally weaker gate: allows external reporters to # re-trigger triage by providing clarification on needs-info @@ -423,7 +432,9 @@ jobs: set -euo pipefail STAGE_ROLE="$STAGE" case "$STAGE" in - code|fix) STAGE_ROLE="coder" ;; + # qualityflow declares "role: coder" in its harness — it commits + # generated tests to the PR branch, same trust level as code/fix. + code|fix|qualityflow) STAGE_ROLE="coder" ;; esac ROLES=$(yq '.defaults.roles[]' config.yaml 2>/dev/null || echo "") @@ -477,18 +488,19 @@ jobs: --jq '{number, html_url, head: {ref: .head.ref, sha: .head.sha, repo: {full_name: .head.repo.full_name}}, base: {ref: .base.ref, repo: {full_name: .base.repo.full_name}}}') || { - if [[ "${STAGE}" =~ ^(fix|review)$ ]]; then + if [[ "${STAGE}" =~ ^(fix|review|qualityflow)$ ]]; then echo "::error::Failed to fetch PR #${PR_NUMBER} head info" exit 1 fi echo "::warning::Failed to fetch PR #${PR_NUMBER} head info — continuing without PR context" exit 0 } - if [[ "${STAGE}" == "fix" ]]; then + # Mutating agents that push to the PR branch must not run on forks. + if [[ "${STAGE}" =~ ^(fix|qualityflow)$ ]]; then HEAD_REPO=$(printf '%s' "${PR_JSON}" | jq -r '.head.repo.full_name') BASE_REPO=$(printf '%s' "${PR_JSON}" | jq -r '.base.repo.full_name') if [[ "${HEAD_REPO}" != "${BASE_REPO}" ]]; then - echo "::error::Fork PR detected (head=${HEAD_REPO}, base=${BASE_REPO}) — fix agent blocked" + echo "::error::Fork PR detected (head=${HEAD_REPO}, base=${BASE_REPO}) — ${STAGE} agent blocked" exit 1 fi fi