Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Ask yourself:
that logic lives.
- [ ] **Both:** Some features may require changes in both.
- [ ] **Generated scripts:** `scripts/pre-code.sh`, `scripts/post-code.sh`,
`scripts/post-fix.sh`, `scripts/post-prioritize.sh`,
`scripts/post-fix.sh`, `scripts/pre-prioritize.sh`, `scripts/post-prioritize.sh`,
`scripts/pre-review.sh`, `scripts/post-review.sh`,
`scripts/pre-triage.sh`, and `scripts/post-triage.sh` are generated
from the corresponding `scripts/<name>.src.sh` — edit the `.src.sh`
Expand Down
22 changes: 15 additions & 7 deletions LOCAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,29 @@ GitHub issue.

### 1. Set environment variables

Export the variables the agent needs:
Export the variables the agent needs. The issue URL and token vars
depend on which forge you're testing against:

**GitHub:**

```bash
# GitHub:
export GITHUB_ISSUE_URL="https://github.com/your-org/test-repo/issues/25"
export GH_TOKEN="$(gh auth token)"
export FULLSEND_FORGE="github"
```

# GitLab (alternative — set these INSTEAD of the GitHub vars above;
# only one forge's vars should be set at a time, and FULLSEND_FORGE
# must match the chosen forge):
# export GITLAB_ISSUE_URL="https://gitlab.com/your-group/test-project/-/issues/25"
# export GITLAB_TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"
# export FULLSEND_FORGE="gitlab"
**GitLab:**

```bash
export GITLAB_ISSUE_URL="https://gitlab.com/your-group/test-project/-/issues/25"
export GITLAB_TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"
export FULLSEND_FORGE="gitlab"
```

**Common (both forges):**

```bash
# GCP/Vertex AI credentials — required by most agents via
# common/env/gcp-vertex.env and the host_files GOOGLE_APPLICATION_CREDENTIALS
# mount in harness YAML.
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.DEFAULT_GOAL := help
.PHONY: help script-build check-bundle script-test test

BUNDLE_SRCS := scripts/pre-code.src.sh scripts/post-code.src.sh scripts/post-fix.src.sh scripts/post-prioritize.src.sh scripts/pre-review.src.sh scripts/post-review.src.sh scripts/pre-triage.src.sh scripts/post-triage.src.sh
BUNDLE_SRCS := scripts/pre-code.src.sh scripts/post-code.src.sh scripts/post-fix.src.sh scripts/pre-prioritize.src.sh scripts/post-prioritize.src.sh scripts/pre-review.src.sh scripts/post-review.src.sh scripts/pre-triage.src.sh scripts/post-triage.src.sh
BUNDLE_OUTS := $(BUNDLE_SRCS:.src.sh=.sh)
LIB_DEPS := $(wildcard scripts/lib/*.lib.sh)

Expand Down Expand Up @@ -48,6 +48,7 @@ script-test:
$(call run-timed,bash scripts/labels-test.sh)
$(call run-timed,bash scripts/post-triage-test.sh)
$(call run-timed,bash scripts/pre-triage-test.sh)
$(call run-timed,bash scripts/pre-prioritize-test.sh)
$(call run-timed,bash scripts/post-prioritize-test.sh)
$(call run-timed,bash scripts/pre-code-test.sh)
$(call run-timed,bash scripts/post-code-test.sh)
Expand Down
24 changes: 12 additions & 12 deletions agents/prioritize.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
---
name: prioritize
description: Score a GitHub issue using the RICE framework (Reach, Impact, Confidence, Effort) and produce structured scores with reasoning.
description: Score an issue using the RICE framework (Reach, Impact, Confidence, Effort) and produce structured scores with reasoning.
skills:
Comment thread
ggallen marked this conversation as resolved.
Comment thread
ggallen marked this conversation as resolved.
Comment thread
ggallen marked this conversation as resolved.
Comment thread
ggallen marked this conversation as resolved.
- customer-research # extension point: provided by target repos, not built into this repo
tools: Bash(gh,jq)
# curl: required by GitLab forge. On GitHub, the network policy binary
# allowlist (policies/github/prioritize.yaml) excludes **/curl, preventing
# it from making network requests even though it is granted here.
tools: Bash(gh,curl,jq)
model: opus
---

You are a prioritization agent. Your job is to evaluate a single GitHub
issue and produce RICE scores that will be used to rank it on the
project board.
You are a prioritization agent. Your job is to evaluate a single issue
and produce RICE scores that will be used to rank it on the project
board.

## Inputs

- `GITHUB_ISSUE_URL` — the HTML URL of the issue (e.g., `https://github.com/org/repo/issues/42`).
- `ISSUE_URL` — the HTML URL of the issue (e.g., `https://github.com/org/repo/issues/42` or `https://gitlab.com/group/project/-/issues/42`).

## Step 1: Fetch the issue

```
gh issue view "$GITHUB_ISSUE_URL" --json number,title,body,labels,assignees,createdAt,updatedAt,author,comments,state,milestone
```

If the command fails, write a JSON error result and stop.
Use the forge-appropriate command from your forge skill to fetch the
issue. If the command fails, write a JSON error result and stop.

## Step 2: Gather context

Expand Down Expand Up @@ -135,7 +135,7 @@ Write the result as JSON to `$FULLSEND_OUTPUT_DIR/agent-result.json`.
re-run the check. If it still fails after 3 attempts, write the best
JSON you have and exit.
- Do NOT post comments, apply labels, or modify the issue in any way.
Your only output is the JSON file. A post-script handles all GitHub
Your only output is the JSON file. A post-script handles all forge
mutations.
- Use the exact scales defined above. Do not invent intermediate
values outside the documented ranges.
Expand Down
50 changes: 43 additions & 7 deletions docs/prioritize.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

![Prioritize agent icon](icons/prioritize.png)

Scores a GitHub issue using the RICE framework (Reach, Impact, Confidence, Effort) and produces scores with reasoning for project board ranking.
Scores an issue using the RICE framework (Reach, Impact, Confidence, Effort) and produces scores with reasoning for project board ranking. Supports both GitHub and GitLab forges.

On GitHub, scores are written to Projects V2 custom fields and posted as a reasoning comment. On GitLab, scores are posted as a sticky issue comment. GitLab custom fields API integration is deferred — the Issues API silently ignores unknown keys, and the correct Custom Fields API requires Premium/Ultimate with runtime field ID resolution.

## Setup

Expand All @@ -16,17 +18,17 @@ No additional setup is required beyond the standard fullsend configuration.

## Triggers

The prioritize agent runs on a schedule, polling the project board for unscored or stale issues.
On GitHub, the prioritize agent runs on a schedule, polling the project board for unscored or stale issues. On GitLab, it is triggered manually.

It can also be triggered manually with the `/fs-prioritize` command.
It can also be triggered manually on either forge with the `/fs-prioritize` command.

## Commands

| Command | Where | Effect |
|---------|-------|--------|
| `/fs-prioritize` | Issue comment | Runs RICE scoring on the issue |

Requires write-level repository permission (admin, maintain, or write).
Requires write-level repository permission.

The `/fs-prioritize` command does not accept arguments. It scores the issue
using the current content, comments, and any available `customer-research`
Expand All @@ -35,7 +37,9 @@ skill data.
## Control labels

The prioritize agent does not apply or consume control labels. It reads the
issue content and produces a score — the project board is updated directly.
issue content and produces a score. On GitHub, the project board is updated
directly via Projects V2 custom fields. On GitLab, scores are posted as
a sticky comment on the issue.

## Configuration

Expand All @@ -62,11 +66,43 @@ about it" (Reach 2.0), instead of guessing from the issue text alone.

### Variables

None.
| Variable | Description | Default | Valid values |
|----------|-------------|---------|--------------|
| `FULLSEND_FORGE` | Forge platform. Set automatically by the harness `forge.<platform>.env` section. | (set by harness) | `"github"`, `"gitlab"` |

## How the agent works

The prioritize agent fetches the issue and all its context, then evaluates it across the four RICE dimensions. It can invoke customer-research skills to gather additional signal about reach and impact. The output is a structured JSON result with per-dimension scores and written reasoning, which the post-script uses to update the project board.
The prioritize agent fetches the issue and all its context, then evaluates it across the four RICE dimensions. It can invoke customer-research skills to gather additional signal about reach and impact. The output is a structured JSON result with per-dimension scores and written reasoning, which the post-script uses to update scores on the forge (GitHub Projects V2 fields or GitLab issue comment).

### Migration notes for custom harness overrides

If you use `base:` composition to override `harness/prioritize.yaml`:

- **`ISSUE_URL` replaces `GITHUB_ISSUE_URL` inside scripts**: The sandbox and
runner env var consumed by pre/post scripts is now `ISSUE_URL` (forge-neutral).
`GITHUB_ISSUE_URL` remains the workflow-level input for the GitHub forge; the
harness maps it to `ISSUE_URL` via `env.runner` / `env.sandbox`. Custom
pre/post scripts that reference `GITHUB_ISSUE_URL` directly should switch to
`ISSUE_URL`.
- **`FULLSEND_FORGE` is required**: Pre- and post-scripts require this env var
to select the correct forge operations. It is set automatically by the forge
sections in the harness; if your override removes the forge sections, set it
explicitly in `env.runner` and `env.sandbox`.
- **`ORG` and `PROJECT_NUMBER` are now optional**: These were previously
hard-required; they are now soft-optional. When unset, the project
board update is skipped and scores are posted as a comment only.
- **`policy`, `skills`, and `host_files` live in forge sections**: This
harness defines policy, skills, and the forge-specific env file
(`env/github/prioritize.env` / `env/gitlab/prioritize.env`) under
`forge.<platform>` rather than at the top level.

### GitLab host allowlist

The GitLab forge operations validate `GITLAB_HOST` against a built-in
allowlist (`gitlab.com`, `gitlab.cee.redhat.com`). To support a
self-hosted GitLab instance, add the hostname to the `case` statements
in `scripts/lib/gitlab-prioritize-ops.lib.sh` and the corresponding
network policy endpoints in `policies/gitlab/prioritize.yaml`.

## Custom network policy

Expand Down
3 changes: 3 additions & 0 deletions env/github/prioritize.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export ISSUE_URL="${GITHUB_ISSUE_URL}"
export GH_TOKEN="${GH_TOKEN}"
export FULLSEND_FORGE="github"
3 changes: 3 additions & 0 deletions env/gitlab/prioritize.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export ISSUE_URL="${GITLAB_ISSUE_URL}"
export GITLAB_TOKEN="${GITLAB_TOKEN}"
export FULLSEND_FORGE="gitlab"
32 changes: 28 additions & 4 deletions harness/prioritize.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,43 @@ timeout_minutes: 10

forge:
github:
policy: policies/github/prioritize.yaml
Comment thread
ggallen marked this conversation as resolved.
providers:
- providers/github-ro.yaml
openshell:
profiles:
- profiles/fullsend-github-ro.yaml
pre_script: scripts/pre-prioritize.sh
post_script: scripts/post-prioritize.sh
skills:
- skills/github-forge
host_files:
- src: env/github/prioritize.env
dest: /sandbox/workspace/.env.d/prioritize.env
expand: true
env:
runner:
GITHUB_ISSUE_URL: ${GITHUB_ISSUE_URL}
ISSUE_URL: ${GITHUB_ISSUE_URL}
GH_TOKEN: ${GH_TOKEN}
ORG: ${ORG}
PROJECT_NUMBER: ${PROJECT_NUMBER}
FULLSEND_FORGE: github
sandbox:
GITHUB_ISSUE_URL: "${GITHUB_ISSUE_URL}"
ISSUE_URL: "${GITHUB_ISSUE_URL}"
GH_TOKEN: "${GH_TOKEN}"
FULLSEND_FORGE: github
gitlab:
policy: policies/gitlab/prioritize.yaml
skills:
- skills/gitlab-forge
host_files:
- src: env/gitlab/prioritize.env
dest: /sandbox/workspace/.env.d/prioritize.env
expand: true
env:
runner:
ISSUE_URL: ${GITLAB_ISSUE_URL}
GITLAB_TOKEN: ${GITLAB_TOKEN}
FULLSEND_FORGE: gitlab
sandbox:
ISSUE_URL: "${GITLAB_ISSUE_URL}"
GITLAB_TOKEN: "${GITLAB_TOKEN}"
FULLSEND_FORGE: gitlab
64 changes: 64 additions & 0 deletions policies/github/prioritize.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
version: 1

# Sandbox policy for the prioritize agent (GitHub forge).
#
# Read-only agent: needs GitHub API (gh issue view, gh project view)
# and Vertex AI for inference. No write access to GitHub — the post-script
# handles mutations on the runner with a separate write-scoped token.
# curl excluded from the binary allowlist to prevent raw HTTP access
# with the injected GH_TOKEN. The agent frontmatter grants curl at the
# tool layer (needed by GitLab forge), but this policy's binary
# allowlist is the enforced control that blocks it on GitHub.

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
Comment thread
ggallen marked this conversation as resolved.
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-only
# gh CLI uses POST /graphql for reads; protocol: graphql allows
# queries but blocks mutations at the AST level
- host: "api.github.com"
port: 443
protocol: graphql
enforcement: enforce
access: read-only
path: "/graphql"
- host: "github.com"
port: 443
protocol: rest
enforcement: enforce
access: read-only
binaries:
- path: "**/gh"
- path: "**/node"
57 changes: 57 additions & 0 deletions policies/gitlab/prioritize.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
version: 1

# Sandbox policy for the prioritize agent (GitLab forge).
#
# Read-only agent: needs GitLab API for issue data and Vertex AI for inference.
# No write access to GitLab — the post-script handles mutations on the
# runner with a separate write-scoped token.
# gh excluded from the binary allowlist — only curl is permitted for
# GitLab API access.

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
Comment thread
ggallen marked this conversation as resolved.
access: read-write
- host: "*.googleapis.com"
port: 443
protocol: rest
enforcement: enforce
access: read-write
binaries:
- path: "**/claude"
- path: "**/node"

gitlab_api:
name: gitlab-api
endpoints:
Comment thread
ggallen marked this conversation as resolved.
Comment thread
ggallen marked this conversation as resolved.
Comment thread
ggallen marked this conversation as resolved.
Comment thread
ggallen marked this conversation as resolved.
Comment thread
ggallen marked this conversation as resolved.
- host: "gitlab.com"
port: 443
protocol: rest
Comment thread
ggallen marked this conversation as resolved.
enforcement: enforce
access: read-only
allow_encoded_slash: true
# Red Hat internal GitLab — supported deployment target for enrolled repos
- host: "gitlab.cee.redhat.com"
port: 443
protocol: rest
enforcement: enforce
access: read-only
allow_encoded_slash: true
binaries:
- path: "**/curl"
- path: "**/node"
Loading
Loading