-
Notifications
You must be signed in to change notification settings - Fork 59
Feat: Demo changes for FullSend Skill Testing #370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
bafddca
db2d096
17773f4
6a60581
08984b9
5686c8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # .fullsend/harness/review.yaml | ||
| # | ||
| # Composes the upstream fullsend review harness (pinned to agents v0.38.0) and | ||
| # adds the local coderabbit-review skill as a complementary finding source. | ||
| # v0.38.0 review harness includes all built-in dimensions (correctness, protected | ||
| # paths, intent/coherence, challenger pass). This harness extends it to also | ||
| # ingest CodeRabbit findings via GitHub comments (S3 pattern) for synthesis. | ||
| # | ||
| # CodeRabbit findings are mapped with coderabbit-* category prefixes so the | ||
| # synthesizer can dedupe and attribute them properly. The built-in review | ||
| # dimensions remain authoritative for the verdict. | ||
| # | ||
| # Trigger: Inherits from base harness (no override needed - /fs-review works | ||
| # automatically for generated PRs per https://fullsend.sh/docs/guides/user/bugfix-workflow#slash-commands) | ||
| base: https://raw.githubusercontent.com/fullsend-ai/agents/48511880eaea5ef01f80b69ba4f228147611db33/harness/review.yaml#sha256=260bf4939f52ef173911d3d788f85bd62b26802f09711d8dcdcce8dbb24c5f04 | ||
|
|
||
| allowed_remote_resources: [https://raw.githubusercontent.com/fullsend-ai/agents/] | ||
|
|
||
| # Extend base skills with repo-specific coderabbit-review skill | ||
| skills: | ||
| - skills/coderabbit-review | ||
|
|
||
| # Note: Required env vars should be set before running fullsend: | ||
| # export PRIOR_REVIEW_SHA="" | ||
| # export PRIOR_REVIEW_PROVENANCE="none" | ||
| # export REPO_FULL_NAME="openshift/ocm-agent-operator" | ||
| # export PR_NUMBER="123" | ||
| # export GH_TOKEN="$(gh auth token)" | ||
| # | ||
| # S2 (CLI mode) - CodeRabbit API key configuration: | ||
| # The CODERABBIT_API_KEY must be set as a CI secret in your pipeline | ||
| # (OpenShift Prow, Tekton, GitHub Actions, etc.) and exposed to the | ||
| # runner-side pre-script that generates coderabbit-findings.json. | ||
| # NEVER inject this secret into the sandbox - only the runner-side | ||
| # pre-script should have access to it. | ||
| # | ||
| # Example Tekton secret: | ||
| # apiVersion: v1 | ||
| # kind: Secret | ||
| # metadata: | ||
| # name: coderabbit-api-key | ||
| # data: | ||
| # api-key: <base64-encoded-key> | ||
| # | ||
| # Example pre-script invocation in CI: | ||
| # export CODERABBIT_API_KEY="$(cat /path/to/secret)" | ||
| # export CODERABBIT_MODE="cli" | ||
| # .fullsend/skills/coderabbit-review/scripts/run-coderabbit.sh "$PR_NUMBER" \ | ||
| # > /tmp/workspace/coderabbit-findings.json | ||
| # | ||
| # S2 (CLI mode) host_files mapping for CodeRabbit findings: | ||
| host_files: | ||
| - src: /tmp/workspace/coderabbit-findings.json | ||
| dest: /sandbox/workspace/coderabbit-findings.json | ||
| optional: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| --- | ||
| name: coderabbit-review | ||
| description: >- | ||
| Ingest CodeRabbit findings for the current PR and map them into FullSend | ||
| review findings for synthesis. Use during /fs-review after the built-in | ||
| dimension sub-agents return, as a complementary (not replacement) source. | ||
| --- | ||
|
|
||
| # CodeRabbit Review Skill | ||
|
|
||
| This skill adds CodeRabbit's AI review as an **extra finding source** for the | ||
| FullSend review agent. It does **not** replace `code-review` / `pr-review` — | ||
| those own protected-path checks, intent/coherence, the challenger pass, and the | ||
| JSON the post-script consumes. This skill only *gathers* CodeRabbit findings and | ||
| *maps* them into the same finding shape so the orchestrator can synthesise them. | ||
|
|
||
| > Naming: this skill is intentionally named `coderabbit-review` (a novel name). | ||
| > A repo skill named `code-review` or `pr-review` would be shadowed by the | ||
| > built-in and never invoked (Personal > Project precedence). | ||
|
|
||
| ## When to use | ||
|
|
||
| - During `/fs-review` on a pull request, after the built-in review dimensions | ||
| have produced their findings, to fold in CodeRabbit's findings. | ||
| - Not for local pre-push (`code-review`) unless CodeRabbit results are already | ||
| available for the branch. | ||
|
|
||
| ## Sources (in priority order) | ||
|
|
||
| The CodeRabbit CLI **cannot** run inside the review sandbox (no `coderabbit` | ||
| binary, no `curl`, `api.coderabbit.ai` is not in the network allowlist, and no | ||
| `CODERABBIT_API_KEY` is injected). So this skill never invokes the CLI directly. | ||
| Instead it reads findings that already exist: | ||
|
|
||
| 1. **Injected file (S2, preferred for production):** if | ||
| `/sandbox/workspace/coderabbit-findings.json` exists, read it. A runner-side | ||
| pre-script produced it outside the sandbox; the API key never enters the | ||
| sandbox. | ||
|
|
||
| **CI Setup for S2 mode:** | ||
| - Store `CODERABBIT_API_KEY` as a secret in your CI system (OpenShift Prow, | ||
| Tekton, GitHub Actions, etc.) | ||
| - In your CI pipeline, before the review harness runs, execute the pre-script: | ||
| ```bash | ||
| export CODERABBIT_API_KEY="$(cat /path/to/secret)" | ||
| export CODERABBIT_MODE="cli" | ||
| .fullsend/skills/coderabbit-review/scripts/run-coderabbit.sh "$PR_NUMBER" \ | ||
| > /tmp/workspace/coderabbit-findings.json | ||
| ``` | ||
| - The `host_files` mapping in `review.yaml` copies this into the sandbox | ||
| - **Never** pass the API key into the sandbox environment | ||
|
|
||
| 2. **GitHub ingest (S3, spike default):** otherwise run | ||
| `scripts/run-coderabbit.sh <PR_NUMBER>`, which uses the read-only `gh` client | ||
| already available to the review agent to pull CodeRabbit's existing PR review | ||
| comments. No extra network, binary, or secret required. | ||
|
|
||
| If neither source yields findings, emit a short informational note and continue. | ||
| **Do not fail the whole review because CodeRabbit was unavailable.** | ||
|
|
||
| ## Step 1: Gather CodeRabbit findings | ||
|
|
||
| ```bash | ||
| # Prefer the injected file; fall back to GitHub ingest. | ||
| if [ -f /sandbox/workspace/coderabbit-findings.json ]; then | ||
| cat /sandbox/workspace/coderabbit-findings.json | ||
| else | ||
| scripts/run-coderabbit.sh "$PR_NUMBER" | ||
| fi | ||
| ``` | ||
|
|
||
| The script emits a JSON array of `{source, path, line, body, url}` objects. | ||
|
|
||
| ## Step 2: Map into FullSend review findings | ||
|
|
||
| For each CodeRabbit item, produce a finding object with these fields: | ||
|
|
||
| - `severity` — map CodeRabbit's severity to the review scale; default to a | ||
| low/`info` severity when CodeRabbit does not state one. | ||
| - `category` — prefix with `coderabbit-` (e.g. `coderabbit-correctness`, | ||
| `coderabbit-style`) so the challenger / synthesiser can dedupe against the | ||
| built-in dimensions. | ||
| - `file`, `line` — from the CodeRabbit comment (`null` for PR-level comments). | ||
| - `description` — CodeRabbit's finding text. | ||
| - `remediation` — CodeRabbit's suggested change, if present. | ||
| - `url` — link back to the CodeRabbit comment for human traceability. | ||
|
|
||
| ## Step 3: Respect repo path exclusions | ||
|
|
||
| `.coderabbit.yaml` already excludes `boilerplate/**`, `hack/**`, `vendor/**`, | ||
| `**/testdata/**`, and generated `**/zz_generated.*.go`. Drop any CodeRabbit | ||
| finding whose `path` matches those globs so the review does not re-flag | ||
| generated or vendored code. | ||
|
|
||
| ## Constraints | ||
|
|
||
| - **Do not** write `agent-result.json` / `review-result.json` — `pr-review` is | ||
| the sole producer of the schema-valid verdict. This skill only contributes | ||
| findings for synthesis. | ||
| - **Never** log, echo, or commit `CODERABBIT_API_KEY` or any token. | ||
| - Treat CodeRabbit findings as advisory input, not gate decisions — the | ||
| built-in dimensions and challenger remain authoritative. | ||
|
|
||
| ## Related | ||
|
|
||
| - `scripts/run-coderabbit.sh` — GitHub ingest (S3) and the runner-only CLI (S2) | ||
| mode, with sandbox caveats documented inline. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| #!/usr/bin/env bash | ||
| # Collect CodeRabbit findings for a PR and emit them as JSON on stdout. | ||
| # | ||
| # Modes: | ||
| # comment (default, S3) - read CodeRabbit's existing PR review comments via | ||
| # `gh`. Sandbox-safe: uses only the read-only GitHub | ||
| # client already available to the review agent. No | ||
| # CodeRabbit credentials and no egress to | ||
| # coderabbit.ai. | ||
| # cli (S2) - run the CodeRabbit CLI for a fresh review. RUNNER | ||
| # ONLY. This will FAIL inside the FullSend review | ||
| # sandbox: no `coderabbit` binary, no `curl`, | ||
| # `api.coderabbit.ai` is not in the network allowlist, | ||
| # and CODERABBIT_API_KEY is not injected. Intended to | ||
| # run in a trusted pre-script whose output is copied | ||
| # into the sandbox via `host_files`. | ||
| # | ||
| # Usage: | ||
| # scripts/run-coderabbit.sh <PR_NUMBER> | ||
| # CODERABBIT_MODE=cli scripts/run-coderabbit.sh <PR_NUMBER> | ||
| set -euo pipefail | ||
|
|
||
| # Emit empty JSON array and exit (for non-fatal failures in comment mode) | ||
| emit_empty() { | ||
| echo "[]" | ||
| exit 0 | ||
| } | ||
|
|
||
| REPO="${CODERABBIT_REPO:-openshift/ocm-agent-operator}" | ||
| MODE="${CODERABBIT_MODE:-comment}" | ||
| BOT="${CODERABBIT_BOT:-coderabbitai}" | ||
|
|
||
| die() { echo "error: $*" >&2; exit 1; } | ||
|
|
||
| PR="${1:-}" | ||
| [ -n "$PR" ] || die "usage: run-coderabbit.sh <PR_NUMBER>" | ||
|
|
||
| case "$MODE" in | ||
| comment) | ||
| # Sandbox-compatible: uses only gh and node (both allowlisted). | ||
| # Failures are non-fatal - emit empty array if CodeRabbit data unavailable. | ||
| command -v gh >/dev/null || { echo "warning: gh CLI not found" >&2; emit_empty; } | ||
| command -v node >/dev/null || { echo "warning: node not found" >&2; emit_empty; } | ||
|
|
||
| # Get current PR head commit to filter stale comments. | ||
| head_sha=$(gh api "repos/${REPO}/pulls/${PR}" --jq '.head.sha' 2>/dev/null || true) | ||
| if [ -z "$head_sha" ]; then | ||
| echo "warning: failed to get PR head commit" >&2 | ||
| emit_empty | ||
| fi | ||
|
|
||
| # Fetch review comments and issue comments (non-fatal on failure). | ||
| review_raw=$(gh api --paginate "repos/${REPO}/pulls/${PR}/comments" 2>/dev/null || echo "[]") | ||
| issue_raw=$(gh api --paginate "repos/${REPO}/issues/${PR}/comments" 2>/dev/null || echo "[]") | ||
|
|
||
| # Use node to filter and transform comments (sandbox-compatible). | ||
| node -e " | ||
| const bot = process.argv[1].toLowerCase(); | ||
| const headSha = process.argv[2]; | ||
| const reviewRaw = JSON.parse(process.argv[3]); | ||
| const issueRaw = JSON.parse(process.argv[4]); | ||
|
|
||
| // Filter review comments: exact bot login, current commit only. | ||
| const review = reviewRaw | ||
| .filter(c => c.user?.login?.toLowerCase() === bot) | ||
| .filter(c => c.commit_id === headSha) | ||
| .map(c => ({ | ||
| source: 'coderabbit', | ||
| path: c.path, | ||
| line: c.line ?? c.original_line, | ||
| body: c.body, | ||
| url: c.html_url, | ||
| commit_id: c.commit_id | ||
| })); | ||
|
|
||
| // Filter issue comments: exact bot login. | ||
| const issue = issueRaw | ||
| .filter(c => c.user?.login?.toLowerCase() === bot) | ||
| .map(c => ({ | ||
| source: 'coderabbit', | ||
| path: null, | ||
| line: null, | ||
| body: c.body, | ||
| url: c.html_url | ||
| })); | ||
|
|
||
| console.log(JSON.stringify([...review, ...issue])); | ||
| " "$BOT" "$head_sha" "$review_raw" "$issue_raw" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] argument-injection GitHub API response bodies containing untrusted comment text are passed as CLI arguments to node -e. For PRs with many comments, this could exceed ARG_MAX. Suggested fix: Pipe JSON through stdin instead of command-line arguments. |
||
| ;; | ||
| cli) | ||
| # RUNNER ONLY - see header. Blocked inside the review sandbox. | ||
| # CODERABBIT_API_KEY must be set as a CI secret and exposed to this pre-script. | ||
| # See SKILL.md and review.yaml for CI setup instructions. | ||
| command -v coderabbit >/dev/null || die "coderabbit CLI not found (runner-only; use MODE=comment in-sandbox)" | ||
| command -v node >/dev/null || die "node not found (needed to normalize CLI output)" | ||
| [ -n "${CODERABBIT_API_KEY:-}" ] || die "CODERABBIT_API_KEY not set (must be configured as CI secret; see SKILL.md)" | ||
|
|
||
| # Get CLI output (try JSON format first, fall back to plain text parsing if needed). | ||
| # Adjust flags based on your installed CLI version's capabilities. | ||
| cli_output=$(coderabbit review --format json --pr "$PR" --api-key "$CODERABBIT_API_KEY" 2>/dev/null \ | ||
| || coderabbit review --plain --pr "$PR" --api-key "$CODERABBIT_API_KEY" 2>&1 \ | ||
| || die "coderabbit CLI failed") | ||
|
|
||
| # Normalize CLI output to {source, path, line, body, url}[] format. | ||
| node -e " | ||
| const raw = process.argv[1]; | ||
| let findings = []; | ||
|
|
||
| try { | ||
| // Try parsing as JSON first | ||
| const parsed = JSON.parse(raw); | ||
| findings = (Array.isArray(parsed) ? parsed : [parsed]) | ||
| .filter(f => f.file && f.line && f.message) | ||
| .map(f => ({ | ||
| source: 'coderabbit', | ||
| path: f.file || f.path || null, | ||
| line: f.line || null, | ||
| body: f.message || f.body || f.comment || '', | ||
| url: f.url || null | ||
| })); | ||
| } catch (e) { | ||
| // Plain text fallback: emit empty array (CLI output not parseable) | ||
| console.error('warning: CLI output not in expected JSON format', e.message); | ||
| findings = []; | ||
| } | ||
|
|
||
| console.log(JSON.stringify(findings)); | ||
| " "$cli_output" | ||
| ;; | ||
| *) | ||
| die "unknown CODERABBIT_MODE: $MODE (expected 'comment' or 'cli')" | ||
| ;; | ||
| esac | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,12 +28,16 @@ type NotificationSeverity string | |
|
|
||
| const ( | ||
| SeverityDebug NotificationSeverity = "Debug" | ||
| SeverityWarning NotificationSeverity = "Warning" | ||
| SeverityInfo NotificationSeverity = "Info" | ||
| SeverityMajor NotificationSeverity = "Major" | ||
| SeverityWarning NotificationSeverity = "Warning" // Deprecated: use SeverityModerate | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] deprecation-contract Deprecation of SeverityWarning, SeverityInfo, and SeverityMajor is communicated only via Go source comments. YAML consumers creating CRs have no visibility into the deprecation. |
||
| SeverityInfo NotificationSeverity = "Info" // Deprecated: use SeverityLow | ||
| SeverityMajor NotificationSeverity = "Major" // Deprecated: use SeverityImportant | ||
| SeverityCritical NotificationSeverity = "Critical" | ||
| SeverityError NotificationSeverity = "Error" | ||
| SeverityFatal NotificationSeverity = "Fatal" | ||
|
|
||
| SeverityImportant NotificationSeverity = "Important" | ||
| SeverityModerate NotificationSeverity = "Moderate" | ||
| SeverityLow NotificationSeverity = "Low" | ||
| ) | ||
|
|
||
| // +kubebuilder:validation:Pattern=`^https?:\/\/.+$` | ||
|
|
@@ -59,7 +63,7 @@ type Notification struct { | |
| // References useful for context or remediation - this could be links to documentation, KB articles, etc | ||
| References []NotificationReferenceType `json:"references,omitempty"` | ||
|
|
||
| // +kubebuilder:validation:Enum={"Debug","Info","Warning","Major","Critical","Error","Fatal"} | ||
| // +kubebuilder:validation:Enum={"Debug","Info","Warning","Major","Critical","Error","Fatal","Important","Moderate","Low"} | ||
| // The severity of the Service Log notification | ||
| Severity NotificationSeverity `json:"severity"` | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,13 +74,18 @@ func (r *OcmAgentReconciler) Reconcile(ctx context.Context, request reconcile.Re | |
| // Request object not found, could have been deleted after reconcile request. | ||
| // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. | ||
| // Return and don't requeue | ||
| reqLogger.V(2).Info("OCMAgent resource not found, marking as absent") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] scope-creep The V(2) debug logging additions are orthogonal to the other changes in this PR and should be tracked separately. |
||
| localmetrics.UpdateMetricOcmAgentResourceAbsent() | ||
| return reconcile.Result{}, nil | ||
| } | ||
| // Error reading the object - requeue the request. | ||
| reqLogger.Error(err, "Failed to retrieve OCMAgent. Will retry on next reconcile.") | ||
| reqLogger.Error(err, "Failed to retrieve OCMAgent from API server. Will retry on next reconcile.", | ||
| "namespace", request.Namespace, "name", request.Name) | ||
| return reconcile.Result{}, err | ||
| } | ||
| reqLogger.V(2).Info("Successfully retrieved OCMAgent resource", | ||
| "replicas", instance.Spec.Replicas, | ||
| "fleetMode", instance.Spec.FleetMode) | ||
| localmetrics.ResetMetricOcmAgentResourceAbsent() | ||
|
|
||
| oaohandler, err := r.OCMAgentHandlerBuilder.New() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[low] shell-injection
The $PR variable is interpolated into gh api URL paths without numeric validation. Mitigated by the sandbox environment and gh CLI URL encoding.
Suggested fix: Add validation: [[ "$PR" =~ ^[0-9]+$ ]] || die "PR_NUMBER must be numeric".