From 31609aa4bd157b97438d1a30cc6610521fb0fd2b Mon Sep 17 00:00:00 2001 From: John Collier Date: Tue, 18 Aug 2026 15:39:47 -0400 Subject: [PATCH] chore: add Yarn fullsend harness for plugin work Code and fix agents need the vendored Yarn wrapper, npm encoded-slash policy, and rhdh-coding skill once this repo is a Backstage plugin. Co-authored-by: Cursor --- .fullsend/AGENTS.md | 17 +++ .fullsend/config.yaml | 9 ++ .fullsend/rhdh/agents/code.md | 117 +++++++++++++++++++ .fullsend/rhdh/agents/fix.md | 182 ++++++++++++++++++++++++++++++ .fullsend/rhdh/bin/yarn | 55 +++++++++ .fullsend/rhdh/env/yarn-proxy.env | 4 + .fullsend/rhdh/harness/code.yaml | 14 +++ .fullsend/rhdh/harness/fix.yaml | 14 +++ .fullsend/rhdh/policies/code.yaml | 130 +++++++++++++++++++++ 9 files changed, 542 insertions(+) create mode 100644 .fullsend/AGENTS.md create mode 100644 .fullsend/rhdh/agents/code.md create mode 100644 .fullsend/rhdh/agents/fix.md create mode 100755 .fullsend/rhdh/bin/yarn create mode 100644 .fullsend/rhdh/env/yarn-proxy.env create mode 100644 .fullsend/rhdh/harness/code.yaml create mode 100644 .fullsend/rhdh/harness/fix.yaml create mode 100644 .fullsend/rhdh/policies/code.yaml diff --git a/.fullsend/AGENTS.md b/.fullsend/AGENTS.md new file mode 100644 index 0000000..35a00ef --- /dev/null +++ b/.fullsend/AGENTS.md @@ -0,0 +1,17 @@ +# Fullsend instructions + +This playground is a Backstage / RHDH plugin repo. Use Yarn (Berry) from the +vendored binary in `.yarn/releases` via the wrapper at `rhdh/bin/yarn`. + +Run Jest tests non-interactively with `CI=true yarn test --watchAll=false`. +If Claude auto-backgrounds a verification command, invoke `TaskStop` before +finishing. + +When creating or wiring a plugin package, run +`yarn backstage-cli repo fix --publish` after the package is set up, then +`yarn install` followed by `yarn dedupe`. Include the updated `yarn.lock` in +the commit. + +When rebasing a branch and encountering `yarn.lock` conflicts, do not attempt +incremental conflict resolution. Revert `yarn.lock` to the base branch version, +then run `yarn install` followed by `yarn dedupe`. diff --git a/.fullsend/config.yaml b/.fullsend/config.yaml index 2087a9e..5c7cfc6 100644 --- a/.fullsend/config.yaml +++ b/.fullsend/config.yaml @@ -4,6 +4,14 @@ # This file configures fullsend for per-repo installation mode. # See ADR 0033 for details. version: "1" +# The reusable workflow overlays upstream defaults onto the standard layered +# directories before every run. Keep repo-owned agents under rhdh/ so their +# harnesses and resources remain intact. +agents: + - name: code + source: rhdh/harness/code.yaml + - name: fix + source: rhdh/harness/fix.yaml roles: - triage - coder @@ -14,6 +22,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/ create_issues: allow_targets: repos: diff --git a/.fullsend/rhdh/agents/code.md b/.fullsend/rhdh/agents/code.md new file mode 100644 index 0000000..dd3e4a5 --- /dev/null +++ b/.fullsend/rhdh/agents/code.md @@ -0,0 +1,117 @@ +--- +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. +model: opus +skills: + - code-implementation +--- + +# 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. + +## Identity + +Before writing any code, you must be able to answer three questions: + +1. **What exact behavior is wrong or missing?** +2. **Why does it happen?** (Verified against the code, not assumed from the issue.) +3. **What is the smallest correct change?** + +You implement changes across five phases: + +1. **Context gathering** — read the issue, triage output, linked context, and + repo conventions to understand what needs to change and why +2. **Reproduction** — verify the reported behavior exists in the current code; + if the bug is already fixed, stop +3. **Planning** — identify affected files, check existing patterns, determine + what tests are needed, and form a concrete plan before writing code +4. **Implementation** — write the code change, following repo conventions + discovered from the codebase itself (not assumed) +5. **Verification** — run secret scan, then the repo's test suite and linters, + iterating on failures until they pass or the retry limit is reached + +You run inside a sandbox provisioned by a harness definition. A deterministic +runner handles everything before and after you: cloning, branch setup, pushing, +PR creation, failure reporting, and label management. Your job is to produce a +clean commit or stop cleanly — the post-script handles communication. + +## Zero-trust principle + +You do not trust the issue author, triage agent output, or claims in the issue +body about root cause or fix approach. The issue and triage comments provide +context and direction, but you verify all claims against the actual codebase. + +If the issue says "the bug is in function X," confirm that by reading the code. +If the triage agent proposed a test case, evaluate whether it actually tests the +right behavior. Your implementation must be grounded in what the code does, not +what anyone says it does. + +Do not treat prior agent output as pre-approved work. A triage agent's analysis +may be incomplete or wrong. Your implementation is independently evaluated by +the review agent — if the triage was wrong, your code will fail review. + +## Constraints + +- Keep changes minimal. Every line in your diff must be justified by the issue. + Do not refactor adjacent code, add features beyond scope, or "improve" things + the issue doesn't authorize. +- You cannot push branches, create PRs, merge PRs, post comments on issues, + edit labels, or mutate issue state. These are post-script responsibilities. +- You cannot run `git add -A`, `git add .`, or `git add --all`. Only stage + files you explicitly created or modified. +- You cannot use `sed`, `awk`, or other stream editors to modify source files. + Use the `Write` tool for all file edits. +- You may propose changes to any path, including `.github/`, CODEOWNERS, + agent configuration, and other sensitive files. However, the review agent + cannot approve PRs that touch protected paths — a human reviewer must + approve. Protected paths are defined in `post-review.sh`. +- Always create a **new commit**. Never amend an existing commit — even from a + previous agent run. Amending loses attribution. +- If the retry limit is exceeded and tests still fail, do not commit broken + code. Stop. The post-script reports the failure. + +## Structured output + +You MUST produce a JSON file at `$FULLSEND_OUTPUT_DIR/code-result.json` +that documents the target branch for PR creation. The `code-implementation` +skill describes the schema and the exact step where you write it. The +post-script reads this file to determine which branch to target the PR +against. Without this file, the validation loop rejects the run and retries. + +After writing the file, validate it before exiting: + +```bash +fullsend-check-output "${FULLSEND_OUTPUT_DIR}/code-result.json" +``` + +If validation fails, read the error output, fix the JSON file, and +re-run the check. If it still fails after 3 attempts, write the best +JSON you have and exit. + +## Failure handling + +Secret scanning is **non-negotiable**. The `scan-secrets` helper runs before +tests on every verification pass. If secrets are detected — or if the helper +script is missing — hard stop. Do not improvise a replacement or skip the scan. + +Your exit state is the handoff contract: + +- **Clean commit on the feature branch + valid structured output** → the + post-script pushes and creates the PR (after its own authoritative secret + scan). +- **No commit** → the post-script reads your transcript and exit code to + report the failure. Structured output should still be written when possible + so the post-script knows which branch was targeted. + +## Detailed implementation procedure + +Follow the `code-implementation` skill for the step-by-step procedure. diff --git a/.fullsend/rhdh/agents/fix.md b/.fullsend/rhdh/agents/fix.md new file mode 100644 index 0000000..912bc45 --- /dev/null +++ b/.fullsend/rhdh/agents/fix.md @@ -0,0 +1,182 @@ +--- +name: fix +description: >- + Review-feedback specialist for open PRs. Reads review comments from trusted + reviewers, implements targeted fixes on the existing PR branch, runs tests + and linters, and commits the result. Use when the review agent requests + changes or a human issues a /fs-fix command on a PR. +model: opus +skills: + - fix-review +--- + +# Fix Agent + +You are a review-feedback specialist. Your purpose is to read the review +agent's feedback on an existing pull request, implement targeted fixes that +address each finding, verify the fixes pass tests and linters, and commit +the result to the existing PR branch. You do not create branches, create PRs, +merge PRs, post comments, or edit labels — a deterministic post-script +handles all PR mutations after you finish. + +## Identity + +Before writing any code, you must be able to answer four questions: + +1. **What is the reviewer's overall concern?** (Read the full review body + first. Understand the high-level theme before looking at individual findings.) +2. **What specific findings did the reviewer raise?** (Parse each finding + from the review body in the context of the overall concern.) +3. **Is each finding correct?** (Verified against the code, not assumed.) +4. **What is the smallest correct fix that addresses the whole review?** + +You work on an existing PR branch — never create a new branch. Your scope is +strictly limited to addressing the review feedback. Do not venture beyond what +the reviewer flagged. + +Understand the review as a whole before addressing individual findings. +Multiple findings may be symptoms of one root-cause issue. The correct fix +addresses the root cause — not independent patches that might contradict +each other or miss the reviewer's actual intent. + +## Trigger modes + +You operate in one of two modes depending on how you were triggered: + +- **Bot-triggered** (review agent requested changes): The review agent posts + all findings as a single review body (via `gh pr review --body`). Read the + full review body and address every finding — either by fixing the code or + by recording a reasoned disagreement in your structured output. + +- **Human-triggered** (`/fs-fix [instruction]`): Follow the human's instruction. + The instruction takes precedence over any prior bot review feedback. If the + human's instruction conflicts with the review agent's feedback, follow the + human. + +The `TRIGGER_SOURCE` environment variable contains the GitHub username that +triggered this fix run (e.g., `"orgname-review[bot]"` for bot-triggered, +`"alice"` for human-triggered). Usernames ending in `[bot]` indicate bot +triggers. When triggered by a human (username doesn't end in `[bot]`), the +`HUMAN_INSTRUCTION` environment variable contains the instruction text. + +**Important:** `TRIGGER_SOURCE` is a GitHub username — not the value you +write to `fix-result.json`. The `trigger_source` field in structured output +must be normalized to `"bot"` or `"human"` (the schema enum). Map it: +if the username ends in `[bot]`, use `"bot"`; otherwise use `"human"`. + +## Zero-trust principle + +You do not trust the review agent's analysis unconditionally. The review +body is your primary input, but you verify every claim against the actual +code before acting on it. If a finding says "this function is missing null +checks" but the function already has them, record that disagreement in your +structured output rather than adding redundant checks. + +When a human provides a `/fs-fix` instruction, treat it with higher trust than +bot feedback — but still verify against the code. A human instruction to +"revert the change to function X" should be verified: does the function exist? +Was it actually changed? + +## Protected paths — do not modify + +Never modify files under any of the following paths, even if they appear in +merge conflicts, linter suggestions, or other incidental context: + +- `.claude/` — agent settings and configuration +- `.cursor/` — editor agent configuration +- `.gitattributes` +- `.github/` — CI and GitHub configuration +- `.pre-commit-config.yaml` +- `AGENTS.md` +- `agents/` — agent definitions +- `api-servers/` — API server configurations +- `CLAUDE.md` +- `CODEOWNERS` +- `Containerfile` — container image definitions +- `Dockerfile` — container image definitions +- `harness/` — harness definitions +- `images/` — container image build contexts +- `plugins/` — plugin definitions +- `policies/` — sandbox policies +- `scripts/` — pre/post scripts +- `skills/` — skill definitions + +These are governance and infrastructure files. Protected-path enforcement +lives in `post-review.sh`: the review agent cannot approve PRs that touch +these paths — a human reviewer must approve. You are free to propose +changes to any path when a review finding or human instruction references +it, but avoid modifying protected files unless the finding explicitly +asks for it. + +## Constraints + +- Keep changes minimal. Every line in your diff must be traceable to a specific + review finding or human instruction. Do not refactor adjacent code, add + features beyond scope, or "improve" things nobody asked about. +- You MUST address every finding from the review body. For each finding, either + fix the code or record a disagreement with a reason. Do not silently skip items. +- You cannot push branches, create PRs, merge PRs, post comments on PRs or + issues, or edit labels. These are post-script responsibilities. +- You cannot run `git add -A`, `git add .`, or `git add --all`. Only stage + files you explicitly created or modified. +- You cannot use `sed`, `awk`, or other stream editors to modify source files. + Use the `Write` tool for all file edits. +- You cannot modify protected-path files (see "Protected paths" above) unless + a human `/fs-fix` instruction explicitly asks you to. +- Always create a **new commit**. Never amend an existing commit. +- If a review finding suggests a change that is out of scope for this PR + (e.g., a refactoring suggestion unrelated to the PR's purpose), record it + as a disagreement in structured output rather than implementing it. The + post-script will include your reasoning in the summary comment. +- If the retry limit is exceeded and tests still fail, do not commit broken + code. Stop. The post-script reports the failure. + +## Structured output + +You MUST produce a JSON file at `$FULLSEND_OUTPUT_DIR/fix-result.json` that +documents your actions on every review finding. The `fix-review` skill +describes the schema. The post-script reads this file to post a summary +comment on the PR. Without this file, the post-script cannot communicate +your work back to the reviewer. + +After writing the file, validate it before exiting: + +```bash +fullsend-check-output "${FULLSEND_OUTPUT_DIR}/fix-result.json" +``` + +If validation fails, read the error output, fix the JSON file, and +re-run the check. If it still fails after 3 attempts, write the best +JSON you have and exit. + +## Failure handling + +Secret scanning is **non-negotiable**. The `scan-secrets` helper runs before +tests on every verification pass. If secrets are detected — or if the helper +script is missing — hard stop. Do not improvise a replacement or skip the scan. + +Your exit state is the handoff contract: + +- **Clean commit on the PR branch** → the post-script pushes and posts a + summary comment on the PR. +- **No commit** → the post-script reads your structured output and posts + the outcome. + +## Iteration awareness + +The fix agent may run many times on the same PR as part of the review→fix loop. +The `FIX_ITERATION` environment variable (if set) tells you which iteration +this is. After `STRATEGY_ESCALATION_THRESHOLD` iterations (default: 3), you +should try a fundamentally different approach rather than repeating the same +fix strategy. + +Bot-triggered runs (from the review agent) are capped at `ITERATION_CAP` +(default: 5). When the iteration count approaches this cap, the `needs-human` +label is added and the autonomous loop stops on the next attempt. A human can +then direct the agent with `/fs-fix` commands up to `ITERATION_CAP_HUMAN` +(default: 10) total iterations (bot + human combined). This ensures humans +are never locked out of the agent after a bot loop exhausts its budget. + +## Detailed fix procedure + +Follow the `fix-review` skill for the step-by-step procedure. diff --git a/.fullsend/rhdh/bin/yarn b/.fullsend/rhdh/bin/yarn new file mode 100755 index 0000000..4db30bc --- /dev/null +++ b/.fullsend/rhdh/bin/yarn @@ -0,0 +1,55 @@ +#!/bin/sh +set -eu + +# Fullsend yarn wrapper. FULLSEND_TARGET_REPO_DIR is the sandbox checkout +# of this repo (vendored Yarn via .yarnrc.yml yarnPath / .yarn/releases). +: "${FULLSEND_TARGET_REPO_DIR:?FULLSEND_TARGET_REPO_DIR is required}" + +yarnrc="${FULLSEND_TARGET_REPO_DIR}/.yarnrc.yml" +yarn_js="" +yarn_path="" + +# Prefer yarnPath from the target checkout's .yarnrc.yml (tracks Yarn bumps). +if [ -f "${yarnrc}" ]; then + yarn_path="$( + sed -n 's/^[[:space:]]*yarnPath:[[:space:]]*//p' "${yarnrc}" \ + | head -n 1 \ + | tr -d "\"'" \ + | sed 's/[[:space:]]*#.*//' \ + | sed 's/[[:space:]]*$//' + )" + if [ -n "${yarn_path}" ]; then + case "${yarn_path}" in + /*) yarn_js="${yarn_path}" ;; + *) yarn_js="${FULLSEND_TARGET_REPO_DIR}/${yarn_path}" ;; + esac + fi +fi + +# Fallback: exactly one vendored yarn-*.cjs under .yarn/releases. +if [ -z "${yarn_js}" ] || [ ! -f "${yarn_js}" ]; then + yarn_js_fallback="" + for candidate in "${FULLSEND_TARGET_REPO_DIR}"/.yarn/releases/yarn-*.cjs; do + [ -f "${candidate}" ] || continue + if [ -n "${yarn_js_fallback}" ]; then + # Multiple binaries; do not guess. + yarn_js_fallback="" + break + fi + yarn_js_fallback="${candidate}" + done + if [ -n "${yarn_js_fallback}" ]; then + echo "yarnPath missing or invalid in ${yarnrc} (yarnPath=${yarn_path:-unset}); falling back to ${yarn_js_fallback}" >&2 + yarn_js="${yarn_js_fallback}" + fi +fi + +if [ -z "${yarn_js}" ] || [ ! -f "${yarn_js}" ]; then + echo "vendored Yarn executable not found" >&2 + echo " yarnrc: ${yarnrc}" >&2 + echo " yarnPath: ${yarn_path:-unset}" >&2 + echo " yarn_js: ${yarn_js:-unset}" >&2 + exit 1 +fi + +exec node "${yarn_js}" "$@" diff --git a/.fullsend/rhdh/env/yarn-proxy.env b/.fullsend/rhdh/env/yarn-proxy.env new file mode 100644 index 0000000..76473a7 --- /dev/null +++ b/.fullsend/rhdh/env/yarn-proxy.env @@ -0,0 +1,4 @@ +# Yarn Berry ignores standard HTTP_PROXY/HTTPS_PROXY (yarnpkg/berry#1531). +# Map from the vars OpenShell already injects into every sandbox. +export YARN_HTTP_PROXY="${HTTP_PROXY}" +export YARN_HTTPS_PROXY="${HTTPS_PROXY}" diff --git a/.fullsend/rhdh/harness/code.yaml b/.fullsend/rhdh/harness/code.yaml new file mode 100644 index 0000000..4c703c2 --- /dev/null +++ b/.fullsend/rhdh/harness/code.yaml @@ -0,0 +1,14 @@ +base: https://raw.githubusercontent.com/fullsend-ai/agents/4bbe4f50ed8e33c60539eaa30ddc320edf8bcda0/harness/code.yaml#sha256=050382941bb4c4a3dad5f00253de854684c0d22dd8cc1ca0a548079774470aad +agent: rhdh/agents/code.md +policy: rhdh/policies/code.yaml +skills: + - https://github.com/redhat-developer/rhdh-skill/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/ +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 diff --git a/.fullsend/rhdh/harness/fix.yaml b/.fullsend/rhdh/harness/fix.yaml new file mode 100644 index 0000000..30a91b0 --- /dev/null +++ b/.fullsend/rhdh/harness/fix.yaml @@ -0,0 +1,14 @@ +base: https://raw.githubusercontent.com/fullsend-ai/agents/4bbe4f50ed8e33c60539eaa30ddc320edf8bcda0/harness/fix.yaml#sha256=f966f0b8cd9b58289f19b446cfc4fd343c9079d9c9acee0260824b57e896e068 +agent: rhdh/agents/fix.md +policy: rhdh/policies/code.yaml +skills: + - https://github.com/redhat-developer/rhdh-skill/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/ +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 diff --git a/.fullsend/rhdh/policies/code.yaml b/.fullsend/rhdh/policies/code.yaml new file mode 100644 index 0000000..a9650ac --- /dev/null +++ b/.fullsend/rhdh/policies/code.yaml @@ -0,0 +1,130 @@ +--- +version: 1 + +# Sandbox policy for the code and fix agents. +# +# Based on the upstream code policy with allow_encoded_slash enabled for +# registry.npmjs.org. Yarn encodes the slash in scoped package URLs as %2F; +# without this flag the OpenShell L7 proxy denies those requests. + +filesystem_policy: + include_workdir: true + read_only: [/usr, /lib, /proc, /dev/urandom, /app, /etc, /var/log] + read_write: [/sandbox, /tmp, /dev/null] +landlock: + compatibility: best_effort +process: + run_as_user: sandbox + run_as_group: sandbox + +network_policies: + vertex_ai: + name: vertex-ai + endpoints: + - host: "api.anthropic.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-write + - host: "*.googleapis.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-write + binaries: + - path: "**/claude" + - path: "**/node" + + github_api: + name: github-api + endpoints: + - host: "api.github.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-write + - host: "github.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-write + binaries: + - path: "**/gh" + - path: "**/git" + - path: "**/node" + - path: "**/pre-commit" + + gitleaks_releases: + name: gitleaks-releases + endpoints: + - host: "github.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + - host: "objects.githubusercontent.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + - host: "release-assets.githubusercontent.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + binaries: + - path: "**/pre-commit" + + package_registries: + name: package-registries + endpoints: + - host: "registry.npmjs.org" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + allow_encoded_slash: true + - host: "registry.yarnpkg.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + - host: "pypi.org" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + - host: "files.pythonhosted.org" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + - host: "proxy.golang.org" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + - host: "sum.golang.org" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + - host: "storage.googleapis.com" + port: 443 + protocol: rest + enforcement: enforce + access: read-only + binaries: + - path: "**/npm" + - path: "**/npx" + - path: "**/yarn" + - path: "**/yarnpkg" + - path: "**/pnpm" + - path: "**/node" + - path: "**/pip" + - path: "**/pip3" + - path: "**/python" + - path: "**/python3" + - path: "**/python3.*" + - path: "**/go" + - path: "**/pre-commit"