diff --git a/.fullsend/config.yaml b/.fullsend/config.yaml index f8ce63e298d..e37a5110b70 100644 --- a/.fullsend/config.yaml +++ b/.fullsend/config.yaml @@ -8,6 +8,8 @@ version: "1" # directories before every run. Keep repo-owned agents under rhdh/ so their # harnesses and resources remain intact. agents: + - name: triage + source: rhdh/harness/triage-jira.yaml - name: code source: rhdh/harness/code.yaml - name: fix @@ -24,7 +26,7 @@ roles: allowed_remote_resources: - https://raw.githubusercontent.com/fullsend-ai/fullsend/ - https://raw.githubusercontent.com/fullsend-ai/agents/ - - https://github.com/redhat-developer/rhdh-skill/ + - https://github.com/redhat-developer/rhdh-skills/ create_issues: allow_targets: repos: diff --git a/.fullsend/rhdh/agents/code.md b/.fullsend/rhdh/agents/code.md index dd3e4a570a9..1367cfaa4be 100644 --- a/.fullsend/rhdh/agents/code.md +++ b/.fullsend/rhdh/agents/code.md @@ -1,9 +1,10 @@ --- name: code description: >- - Implementation specialist for GitHub issues. Reads triaged issues, implements - fixes following repo conventions, runs tests and linters, and commits to a - feature branch. Use when implementing a fix or feature from a triaged issue. + Implementation specialist for GitHub or Jira issues. Reads triaged issues, + implements fixes following repo conventions, runs tests and linters, and + commits to a feature branch. Use when implementing a fix or feature from a + triaged issue. model: opus skills: - code-implementation @@ -12,11 +13,11 @@ skills: # Code Agent You are an implementation specialist. Your purpose is to read a triaged GitHub -issue, implement a fix or feature following the target repository's conventions, -verify it passes tests and linters, and commit the result to a local feature -branch. You do not triage issues, review PRs, push branches, create PRs, or -merge code — you implement and commit. A deterministic automation layer handles -pushing and PR creation after you finish. +issue or Jira work item, implement a fix or feature following the target +repository's conventions, verify it passes tests and linters, and commit the +result to a local feature branch. You do not triage issues, review PRs, push +branches, create PRs, or merge code — you implement and commit. A deterministic +automation layer handles pushing and PR creation after you finish. ## Identity @@ -26,6 +27,11 @@ Before writing any code, you must be able to answer three questions: 2. **Why does it happen?** (Verified against the code, not assumed from the issue.) 3. **What is the smallest correct change?** +When `FULLSEND_TRACKER=jira`, use `ISSUE_URL` and the Jira API credentials +provided by the harness to read the work item and its comments. Stop if the +work item cannot be read. Never infer Jira ticket details from a similarly +named GitHub issue. + You implement changes across five phases: 1. **Context gathering** — read the issue, triage output, linked context, and diff --git a/.fullsend/rhdh/harness/code.yaml b/.fullsend/rhdh/harness/code.yaml index 29e9bf11a1b..a297955dc1f 100644 --- a/.fullsend/rhdh/harness/code.yaml +++ b/.fullsend/rhdh/harness/code.yaml @@ -12,13 +12,26 @@ image: ghcr.io/redhat-developer/rhdh-fullsend-code:latest # profiles/fullsend-vertex-ai.yaml now does that upstream (ADR 0065's # provider/profile network policy migration). skills: - - https://github.com/redhat-developer/rhdh-skill/tree/e84109919ae6085e3dcfe568c695e6dda54874ce/skills/rhdh-coding#sha256=5a4bc35476108a215091ccf1180cee68547c7668c6b193de4402674bc7b6cded + - https://github.com/redhat-developer/rhdh-skills/tree/e84109919ae6085e3dcfe568c695e6dda54874ce/skills/rhdh-coding#sha256=5a4bc35476108a215091ccf1180cee68547c7668c6b193de4402674bc7b6cded # Harness-level allowlist required for URL skills (not inherited from base or # config.yaml). Must also be covered by config.yaml allowed_remote_resources. allowed_remote_resources: - - https://github.com/redhat-developer/rhdh-skill/ + - https://github.com/redhat-developer/rhdh-skills/ host_files: - src: rhdh/bin/yarn dest: /sandbox/workspace/bin/yarn - src: rhdh/env/yarn-proxy.env dest: /sandbox/workspace/.env.d/yarn-proxy.env +# A Jira work item has no GitHub issue number. Keep generated PRs on main. +env: + runner: + CODE_ALLOWED_TARGET_BRANCHES: "main" + +# The Jira poller evaluates explicit CEL triggers. GitHub /fs-code continues +# to use Fullsend's built-in stage router. +trigger: > + event.source.system == "jira" + && event.transition.kind == "comment_added" + && has(event.transition.comment.command) + && event.transition.comment.command == "/fs-code" + && event.entity.kind == "work_item" diff --git a/.fullsend/rhdh/harness/fix.yaml b/.fullsend/rhdh/harness/fix.yaml index 59eaef7712a..cb8320482ae 100644 --- a/.fullsend/rhdh/harness/fix.yaml +++ b/.fullsend/rhdh/harness/fix.yaml @@ -12,11 +12,11 @@ image: ghcr.io/redhat-developer/rhdh-fullsend-code:latest # profiles/fullsend-vertex-ai.yaml now does that upstream (ADR 0065's # provider/profile network policy migration). skills: - - https://github.com/redhat-developer/rhdh-skill/tree/e84109919ae6085e3dcfe568c695e6dda54874ce/skills/rhdh-coding#sha256=5a4bc35476108a215091ccf1180cee68547c7668c6b193de4402674bc7b6cded + - https://github.com/redhat-developer/rhdh-skills/tree/e84109919ae6085e3dcfe568c695e6dda54874ce/skills/rhdh-coding#sha256=5a4bc35476108a215091ccf1180cee68547c7668c6b193de4402674bc7b6cded # Harness-level allowlist required for URL skills (not inherited from base or # config.yaml). Must also be covered by config.yaml allowed_remote_resources. allowed_remote_resources: - - https://github.com/redhat-developer/rhdh-skill/ + - https://github.com/redhat-developer/rhdh-skills/ host_files: - src: rhdh/bin/yarn dest: /sandbox/workspace/bin/yarn diff --git a/.fullsend/rhdh/harness/triage-jira.yaml b/.fullsend/rhdh/harness/triage-jira.yaml new file mode 100644 index 00000000000..f9ee5b2293a --- /dev/null +++ b/.fullsend/rhdh/harness/triage-jira.yaml @@ -0,0 +1,18 @@ +# The stock triage harness has no CEL trigger. Jira polling needs an explicit +# trigger so /fs-triage comments are routed to the triage agent. +base: https://raw.githubusercontent.com/fullsend-ai/agents/7ab05564b3c9c0ab20197e0c8572b5905ffd75ca/harness/triage.yaml#sha256=3aa32e9f66ad6a9b60a75f50d3adc6ae39ebfb425c3928381e019436498bf2aa + +allowed_remote_resources: + - https://raw.githubusercontent.com/fullsend-ai/agents/ + +env: + runner: + # Keep coding explicitly requested via /fs-code during this pilot. + TRIAGE_AUTO_CODE: "off" + +trigger: > + event.source.system == "jira" + && event.transition.kind == "comment_added" + && has(event.transition.comment.command) + && event.transition.comment.command == "/fs-triage" + && event.entity.kind == "work_item" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 846c14a87fc..1be2ab12998 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -44,4 +44,5 @@ /.cursor/ @redhat-developer/rhdh-plugins-maintainers @redhat-developer/rhdh-fullsend /.fullsend/ @redhat-developer/rhdh-plugins-maintainers @redhat-developer/rhdh-fullsend /.github/workflows/fullsend.yaml @redhat-developer/rhdh-plugins-maintainers @redhat-developer/rhdh-fullsend +/.github/workflows/fullsend-poll-jira.yml @redhat-developer/rhdh-plugins-maintainers @redhat-developer/rhdh-fullsend /.github/workflows/prioritize.yml @redhat-developer/rhdh-plugins-maintainers @redhat-developer/rhdh-fullsend diff --git a/.github/workflows/fullsend-poll-jira.yml b/.github/workflows/fullsend-poll-jira.yml new file mode 100644 index 00000000000..77abc296d88 --- /dev/null +++ b/.github/workflows/fullsend-poll-jira.yml @@ -0,0 +1,143 @@ +# Poll RHIDP for Fullsend commands intended for this repository. +# +# Fullsend resolves the command author's Jira role in RHIDP. +# Only /fs-triage and /fs-code are registered for +# Jira dispatch; other Fullsend commands remain out of scope. + +name: fullsend Jira poll + +on: + schedule: + - cron: "*/5 * * * *" + workflow_dispatch: {} + +permissions: {} + +jobs: + poll: + runs-on: ubuntu-24.04 + permissions: + contents: read + concurrency: + group: fullsend-jira-poll + cancel-in-progress: false + outputs: + matrix: ${{ steps.build-matrix.outputs.matrix }} + has_dispatches: ${{ steps.build-matrix.outputs.has_dispatches }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: Install fullsend + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + gh release download v0.43.0 --repo fullsend-ai/fullsend \ + -p 'fullsend_0.43.0_linux_amd64.tar.gz' -O - | tar xz + sudo mv fullsend /usr/local/bin/ + fullsend --version + + - name: Poll Jira + env: + JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_BASE_URL: ${{ vars.JIRA_BASE_URL }} + TARGET_REPO: ${{ github.repository }} + run: | + set -euo pipefail + if [[ -z "${JIRA_BASE_URL}" ]]; then + echo "::error::JIRA_BASE_URL variable is empty (must be an Actions variable, not a secret)" + exit 1 + fi + if [[ -z "${JIRA_TOKEN}" || -z "${JIRA_USER_EMAIL}" ]]; then + echo "::error::JIRA_TOKEN and JIRA_USER_EMAIL must be configured as Actions secrets" + exit 1 + fi + + poll_project() { + local project="$1" + local team_id="$2" + local output="dispatches-${project,,}.json" + # Keep each gate explicit. To enroll another team/project, add one + # poll_project call below with its Atlassian team ID. + local jql="project = ${project} AND cf[10001] = \"${team_id}\" AND labels = \"fullsend\" AND statusCategory != Done ORDER BY updated DESC" + echo "Polling ${project} for team ${team_id}; dispatching matches to ${TARGET_REPO}" + fullsend poll \ + --input-driver jira-poll \ + --mode slash \ + --jira-url "${JIRA_BASE_URL}" \ + --jira-project "${project}" \ + --jql "${jql}" \ + --target-repo "${TARGET_REPO}" \ + --output "${output}" \ + --fullsend-dir .fullsend + } + + # Jira Team "RHDH AI" uses this Atlassian ID in cf[10001], not its display name. + RHDH_AI_TEAM_ID='ec74d716-af36-4b3c-950f-f79213d08f71-1087' + poll_project RHIDP "${RHDH_AI_TEAM_ID}" + + jq -s 'add' dispatches-*.json > dispatches.json + echo "Dispatch count: $(jq 'length' dispatches.json)" + jq -c '.[] | {agent, event_type, status_number}' dispatches.json || true + + - name: Upload dispatches.json + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + if: always() + with: + name: dispatches + path: dispatches.json + if-no-files-found: ignore + + - name: Build dispatch matrix + id: build-matrix + run: | + set -euo pipefail + if [[ ! -f dispatches.json ]] || ! jq -e 'length > 0' dispatches.json > /dev/null 2>&1; then + echo "No dispatches." + echo 'matrix={"include":[]}' >> "${GITHUB_OUTPUT}" + echo "has_dispatches=false" >> "${GITHUB_OUTPUT}" + exit 0 + fi + + MATRIX=$(jq -c '{include: .}' dispatches.json) + DELIM="MATRIX_$(openssl rand -hex 8)" + { + echo "matrix<<${DELIM}" + printf '%s' "${MATRIX}" + echo + echo "${DELIM}" + } >> "${GITHUB_OUTPUT}" + echo "has_dispatches=true" >> "${GITHUB_OUTPUT}" + + harness: + name: Harness + needs: poll + if: needs.poll.outputs.has_dispatches == 'true' + permissions: + actions: write + contents: write + id-token: write + issues: write + packages: read + pull-requests: write + uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@d5f36921ac754705619f38c637ef692873809fbc # v0.43.0 + with: + matrix: ${{ needs.poll.outputs.matrix }} + install_mode: per-repo + mint_url: ${{ vars.FULLSEND_MINT_URL }} + gcp_region: ${{ vars.FULLSEND_GCP_REGION }} + jira_base_url: ${{ vars.JIRA_BASE_URL }} + runner_image: ubuntu-24.04 + secrets: + FULLSEND_GCP_WIF_PROVIDER: ${{ secrets.FULLSEND_GCP_WIF_PROVIDER }} + FULLSEND_GCP_PROJECT_ID: ${{ secrets.FULLSEND_GCP_PROJECT_ID }} + OTEL_EXPORTER_OTLP_TRACES_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_TRACES_HEADERS }} + OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }} + JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} diff --git a/docs/fullsend.md b/docs/fullsend.md index 76c453737db..e28c728b018 100644 --- a/docs/fullsend.md +++ b/docs/fullsend.md @@ -2,7 +2,7 @@ ## What is fullsend? -[Fullsend](https://github.com/fullsend-ai/fullsend) is an agentic SDLC platform that provides AI-powered agents for triage, code review, code generation, and retrospectives. It runs as a GitHub Actions pipeline, triggered by GitHub events, and uses Vertex AI (Anthropic Claude) for inference. +[Fullsend](https://github.com/fullsend-ai/fullsend) is an agentic SDLC platform that provides AI-powered agents for triage, code review, code generation, and retrospectives. It runs as a GitHub Actions pipeline, triggered by GitHub events or the Jira poller, and uses Vertex AI (Anthropic Claude) for inference. ## Pilot scope @@ -57,6 +57,35 @@ Available commands: | `/fs-fix` | Fix issues flagged in a review | | `/fs-fix-stop` | Disable fix agent for a PR (adds `fullsend-no-fix` label) | +### Jira pilot for RHDH AI + +The [Jira poll workflow](../.github/workflows/fullsend-poll-jira.yml) checks +every five minutes for `/fs-triage` and `/fs-code` comments on RHIDP work +items. Fullsend checks the command author's Jira project role. The existing +GitHub Fullsend commands are unchanged. + +Each Jira query requires all of these gates: + +- Project: `RHIDP`. +- Jira `Team`: `RHDH AI`, selected by `cf[10001]` and its Atlassian team ID. +- Label: `fullsend` (agent opt-in). +- Status category is not Done. + +Add the `fullsend` label to an eligible Jira work item before posting a +command. `/fs-triage` does not automatically start coding; use `/fs-code` +explicitly when implementation is ready. Grillme and spec commands are not +part of this Jira pilot. + +To expand the cohort, add a project/team pair to the `poll_project` calls in +the workflow. Keep the `fullsend` opt-in label for each cohort. + +Before enabling the scheduled workflow, configure the Actions variable +`JIRA_BASE_URL` and secrets `JIRA_TOKEN` and `JIRA_USER_EMAIL`. The workflow +fails visibly when any are missing. Its existing Fullsend GCP and mint +credentials are also required for the downstream agents. The RHDH AI team ID +comes from Parasol's poller for `https://stage-redhat.atlassian.net`; verify +the ID if `JIRA_BASE_URL` points to a different Jira instance. + ## Coexistence with PR Agent rhdh-plugins already has [PR Agent](https://github.com/Codium-ai/pr-agent) configured (`.pr_agent.toml`). Both agents run independently: