Skip to content

feat: make triage agent multi-forge (GitHub + GitLab) - #686

Merged
ggallen merged 1 commit into
fullsend-ai:mainfrom
ggallen:worktree-gitlab-triage-agent
Aug 14, 2026
Merged

feat: make triage agent multi-forge (GitHub + GitLab)#686
ggallen merged 1 commit into
fullsend-ai:mainfrom
ggallen:worktree-gitlab-triage-agent

Conversation

@ggallen

@ggallen ggallen commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Extract forge-specific operations from pre/post-triage scripts into sourced ops files (scripts/github/triage-ops.sh, scripts/gitlab/triage-ops.sh) with a shared forge_* function interface
  • Reorganize forge-specific files by platform: policies/{github,gitlab}/, env/{github,gitlab}/, skills/{github,gitlab}/, skills/issue-labels/{github,gitlab}/
  • Make agent prompt forge-neutral (ISSUE_URL, generic terminology, delegates CLI commands to forge skills)
  • Update schema to accept both GitHub and GitLab URL patterns
  • Add forge sections to harness config with per-forge policy, skills, host_files, and env vars

Dependencies

Test plan

  • All 112 tests pass (bash scripts/post-triage-test.sh) — 101 GitHub + 11 GitLab
  • Schema validates both GitHub and GitLab URL patterns
  • Existing GitHub behavior preserved (including auto-code config)
  • End-to-end validation on GitLab runner (pending openshell gateway setup)

🤖 Generated with Claude Code

@ggallen
ggallen requested a review from a team as a code owner August 6, 2026 01:07
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:08 AM UTC · Completed 1:26 AM UTC
Commit: e8850dd · View workflow run →

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Make triage agent multi-forge (GitHub + GitLab)

✨ Enhancement ⚙️ Configuration changes 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add forge abstraction for triage mutations via FULLSEND_FORGE-selected ops functions.
• Split harness policy/env/skills configuration into per-forge sections for GitHub and GitLab.
• Extend schema and tests to validate GitLab issue/MR URL support and behavior parity.
Diagram

graph TD
  H["harness/triage.yaml"] --> Pre["pre-triage.sh"] --> Ops["forge triage-ops.sh"] --> API{{"GitHub/GitLab API"}}
  H --> Agent["agents/triage.md"] --> Skills["forge skills (gh/curl)"] --> API
  Agent --> Result[("agent-result.json")] --> Post["post-triage.sh"] --> Ops
  subgraph Legend
    direction LR
    _cfg["Config"] ~~~ _scr["Script"] ~~~ _data[("Data")] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Single script with per-forge case statements
  • ➕ Keeps all behavior in one file; easier to diff GitHub vs GitLab handling
  • ➕ Avoids duplicated function definitions across ops files
  • ➖ Harder to test/maintain as forge surface area grows
  • ➖ Riskier changes: touching one forge can accidentally regress the other
2. Shared shell library + minimal forge adapters
  • ➕ Reduces duplication between GitHub and GitLab ops (common validation, logging, retries)
  • ➕ Provides a clearer contract for future forges (e.g., Bitbucket)
  • ➖ Requires more upfront factoring and careful boundary decisions
  • ➖ May complicate simple per-forge differences (e.g., GitLab label semantics)

Recommendation: The current approach (forge-specific ops files implementing a shared forge_* interface, sourced by pre/post scripts) is the best tradeoff for maintainability and risk isolation. If duplication between GitHub/GitLab ops grows, consider extracting a small shared helper library (logging/retry/JSON helpers) while keeping API semantics in per-forge adapters.

Files changed (16) +981 / -187 · 3 not counted

Enhancement (3) +323 / -3
triage-result.schema.jsonAccept GitLab URL formats and nested project paths in triage results +9/-3

Accept GitLab URL formats and nested project paths in triage results

• Expands URL validation to allow GitLab MR and issue URL patterns in pull_requests and prerequisites. Updates repo pattern to support GitLab subgroup paths (multiple segments).

schemas/triage-result.schema.json

triage-ops.shIntroduce GitHub forge ops (gh-based label/comment/issue actions) +119/-0

Introduce GitHub forge ops (gh-based label/comment/issue actions)

• Implements forge_validate_issue_url/forge_parse_issue_url and a forge_* API for labels, comments, sticky comments, closing, and issue creation using gh/fullsend.

scripts/github/triage-ops.sh

triage-ops.shIntroduce GitLab forge ops (curl-based label/comment/issue actions) +195/-0

Introduce GitLab forge ops (curl-based label/comment/issue actions)

• Implements the same forge_* interface for GitLab using curl against the REST API, including URL parsing, label updates, note posting with marker-based stickiness, closing, and issue creation.

scripts/gitlab/triage-ops.sh

Refactor (2) +70 / -117
post-triage.shRefactor post-triage mutations through forge ops abstraction +57/-91

Refactor post-triage mutations through forge ops abstraction

• Replaces GitHub-specific URL parsing and gh calls with forge_validate_issue_url/forge_parse_issue_url and forge_* operations. Ensures comments, labels, close, and create actions work consistently across GitHub and GitLab.

scripts/post-triage.sh

pre-triage.shRefactor pre-triage label reset through forge ops abstraction +13/-26

Refactor pre-triage label reset through forge ops abstraction

• Switches to ISSUE_URL/FULLSEND_FORGE and delegates label stripping/verification to forge_* functions so the same script can run on GitHub and GitLab.

scripts/pre-triage.sh

Tests (1) +240 / -1
post-triage-test.shAdd GitLab forge test coverage and switch tests to ISSUE_URL +240/-1

Add GitLab forge test coverage and switch tests to ISSUE_URL

• Updates GitHub tests to use ISSUE_URL/FULLSEND_FORGE and adds a new GitLab test suite with a mock curl to verify correct API calls and ensure gh is not invoked for GitLab runs.

scripts/post-triage-test.sh

Documentation (5) +262 / -57
triage.mdMake triage prompt forge-neutral (ISSUE_URL, PR/MR, CI paths) +29/-57

Make triage prompt forge-neutral (ISSUE_URL, PR/MR, CI paths)

• Replaces GitHub-specific language and inputs with a forge-neutral ISSUE_URL and generic terminology. Delegates data fetching/listing/searching commands to forge-specific skills, and broadens workflow-file guidance to include GitLab CI.

agents/triage.md

SKILL.mdAdd GitHub skill with canonical gh commands for triage +63/-0

Add GitHub skill with canonical gh commands for triage

• Documents standard gh commands for issue/PR retrieval, searches, and repo file reads, and shows how to derive REPO/ISSUE_NUMBER from ISSUE_URL.

skills/github/SKILL.md

SKILL.mdAdd GitLab skill with canonical curl API commands for triage +93/-0

Add GitLab skill with canonical curl API commands for triage

• Documents GitLab REST API usage via curl, including parsing ISSUE_URL into host/project/IID and commands for issues, notes, merge requests, and repo contents.

skills/gitlab/SKILL.md

SKILL.mdRelocate GitHub issue-labels skill under forge-specific path not counted

Relocate GitHub issue-labels skill under forge-specific path

• Keeps the GitHub issue-labels skill content available under skills/issue-labels/github/ for per-forge skill selection.

skills/issue-labels/github/SKILL.md

SKILL.mdAdd GitLab variant of issue-labels skill +77/-0

Add GitLab variant of issue-labels skill

• Adds GitLab-specific guidance for discovering labels and recommending label_actions using the GitLab API (projects/*/labels, issues listing).

skills/issue-labels/gitlab/SKILL.md

Other (5) +86 / -9
triage.envPlace GitHub triage env in forge-specific location not counted

Place GitHub triage env in forge-specific location

• Defines the GitHub env exports in env/github/triage.env for use by the GitHub forge harness section.

env/github/triage.env

triage.envAdd GitLab triage env file (issue URL + token) +2/-0

Add GitLab triage env file (issue URL + token)

• Introduces env/gitlab/triage.env exporting GITLAB_ISSUE_URL and GITLAB_TOKEN for GitLab runs.

env/gitlab/triage.env

triage.yamlAdd per-forge triage configuration for GitHub and GitLab +32/-9

Add per-forge triage configuration for GitHub and GitLab

• Moves policy/skills/host_files/env wiring under forge.github and forge.gitlab. Sets ISSUE_URL and FULLSEND_FORGE appropriately and attaches forge-specific policies and skills.

harness/triage.yaml

triage.yamlRelocate GitHub triage sandbox policy under policies/github/ not counted

Relocate GitHub triage sandbox policy under policies/github/

• Keeps the GitHub-specific sandbox policy in a forge-namespaced path to match the new harness layout.

policies/github/triage.yaml

triage.yamlAdd GitLab triage sandbox policy (curl + GitLab endpoints) +52/-0

Add GitLab triage sandbox policy (curl + GitLab endpoints)

• Adds a GitLab-specific sandbox policy that allows curl/node and GitLab API network access, while preserving Vertex AI access for inference.

policies/gitlab/triage.yaml

@qodo-code-review

qodo-code-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Protected paths modified ✗ Dismissed 📜 Skill insight § Compliance
Description
This PR modifies protected governance/infrastructure paths (including agents/, harness/,
policies/, scripts/, skills/, and schemas/), which must not be auto-approved and require
explicit human review. A protected-path finding is mandatory when these directories are changed.
Code

harness/triage.yaml[R35-43]

forge:
  github:
+    policy: policies/github/triage.yaml
    pre_script: scripts/pre-triage.sh
    post_script: scripts/post-triage.sh
+    skills:
+      - skills/github
+      - skills/issue-labels/github
+    host_files:
Relevance

●●● Strong

Protected-path governance findings are expected on harness/infra edits; similar governance notes
appear historically.

PR-#631
PR-#476

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538392 requires raising a finding whenever protected governance/infrastructure
paths are modified. The diff includes changes to protected configuration (harness/triage.yaml) and
related pipeline scripts/policies.

harness/triage.yaml[35-75]
scripts/pre-triage.sh[1-34]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected governance/infrastructure paths are modified in this PR. Per compliance, such PRs must not be auto-approved and should have explicit authorization context.

## Issue Context
Protected paths include agent definitions, harness configs, policies, scripts, skills, and schemas—changes here impact pipeline behavior and security boundaries.

## Fix Focus Areas
- harness/triage.yaml[35-75]
- scripts/pre-triage.sh[1-34]
- scripts/post-triage.sh[1-30]
- policies/gitlab/triage.yaml[1-52]
- schemas/triage-result.schema.json[39-81]
- agents/triage.md[1-75]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. ISSUE_URL unsanitized in ::notice:: ✓ Resolved 📜 Skill insight ⛨ Security
Description
scripts/pre-triage.sh emits a GitHub Actions workflow command with an interpolated ISSUE_URL
that is not sanitized, enabling workflow-command injection via :: sequences, encoded newlines, or
control characters. This violates the requirement that every interpolated value in workflow commands
be sanitized individually.
Code

scripts/pre-triage.sh[22]

+echo "::notice::🔗 Triage target: ${ISSUE_URL}"
Relevance

●●● Strong

Strong precedent accepting sanitization of interpolated values in GitHub Actions workflow commands
to prevent injection.

PR-#592
PR-#573

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538382 requires sanitizing all interpolated variables in GitHub Actions workflow
commands. The updated pre-triage.sh emits ::notice::... while directly interpolating ISSUE_URL
with no sanitization step.

scripts/pre-triage.sh[18-24]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scripts/pre-triage.sh` prints a GitHub Actions workflow command (`::notice::...`) while interpolating `ISSUE_URL` without sanitization. This can allow workflow command injection if `ISSUE_URL` contains `::`, `%0A/%0D`, ANSI escapes, or other control characters.

## Issue Context
The repository already contains a `gha_echo` helper that sanitizes workflow command output in other scripts; `pre-triage.sh` should use the same (or equivalent) sanitization behavior.

## Fix Focus Areas
- scripts/pre-triage.sh[18-25]
- scripts/lib/post-failure-report.lib.sh[25-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. GitLab policy lacks justification ✗ Dismissed 📜 Skill insight ⛨ Security
Description
The new policies/gitlab/triage.yaml expands sandbox/network permissions (e.g., *.googleapis.com,
gitlab.com, gitlab.cee.redhat.com, allowed binaries) without an explicit linked issue/ADR
justification in the policy itself. Permission expansions without explicit authorization and
least-privilege justification violate the compliance requirement.
Code

policies/gitlab/triage.yaml[R28-32]

+      - host: "*.googleapis.com"
+        port: 443
+        protocol: rest
+        enforcement: enforce
+        access: read-write
Relevance

●● Moderate

Permission expansion is security-sensitive, but no clear precedent requiring in-file issue/ADR
justification for policies.

PR-#78

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1538316 requires least-privilege permission changes with explicit justification.
The new GitLab policy declares broad network endpoints (including wildcard *.googleapis.com) and
allowed binaries, but contains no linked issue/ADR authorizing these permissions.

policies/gitlab/triage.yaml[19-52]
Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new permission-declaring policy file is introduced for the GitLab triage forge, granting network access and binary allowances. The change lacks an explicit linked issue/ADR justification and least-privilege rationale alongside the permissions.

## Issue Context
Compliance requires permission expansions (policy manifests, RBAC, workflow permissions, etc.) to be least-privilege and explicitly justified with an authorizing issue/ADR.

## Fix Focus Areas
- policies/gitlab/triage.yaml[1-52]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Repo schema mismatches GitHub 🐞 Bug ≡ Correctness
Description
The schema now allows prerequisites.create.repo values with 3+ path segments, but GitHub
forge_create_issue passes repo directly to gh issue create --repo, which expects OWNER/REPO.
This allows schema-valid outputs that deterministically fail prerequisite issue creation on GitHub
if a multi-segment repo is produced.
Code

schemas/triage-result.schema.json[R80-81]

+                "pattern": "^[a-zA-Z0-9._-]+(/[a-zA-Z0-9._-]+)+$"
              },
Relevance

●●● Strong

Deterministic schema/runtime mismatch; repo has accepted schema consistency/correctness fixes
previously.

PR-#622

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The schema explicitly permits nested repo paths, while the GitHub forge implementation forwards the
repo string directly to gh’s --repo flag and post-triage uses it without GitHub-specific
validation.

schemas/triage-result.schema.json[71-82]
scripts/post-triage.sh[199-201]
scripts/github/triage-ops.sh[114-119]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`prerequisites.create[].repo` is validated by a shared schema that now permits nested paths (needed for GitLab), but the GitHub implementation expects exactly one slash for `--repo OWNER/REPO`.

## Issue Context
This is a contract mismatch between validation (schema) and execution (GitHub ops). The post-triage script calls `forge_create_issue` with `TARGET_REPO` without additional GitHub-specific validation.

## Fix Focus Areas
- schemas/triage-result.schema.json[71-82]
- scripts/github/triage-ops.sh[114-119]
- scripts/post-triage.sh[199-201]

## What to change
- Option A (recommended): keep the schema broad for cross-forge, but add GitHub-side validation in `scripts/github/triage-ops.sh::forge_create_issue`:
 - reject repo values that don’t match `^[^/]+/[^/]+$` and return a clear error.
- Option B: change the schema field to `anyOf`:
 - GitHub repo pattern (`owner/repo`)
 - GitLab project path pattern (`group(/subgroup)+/project`)
 - and additionally validate at runtime based on `FULLSEND_FORGE`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. GitLab label discovery incomplete ✗ Dismissed 🐞 Bug ≡ Correctness
Description
forge_list_repo_labels on GitLab fetches only /labels?per_page=100 with no pagination, so projects
with more than 100 labels will have missing entries. post-triage.sh then incorrectly treats some
real labels as nonexistent and refuses valid label_actions.
Code

scripts/gitlab/triage-ops.sh[R128-130]

+forge_list_repo_labels() {
+  _gitlab_api GET "/projects/${REPO}/labels?per_page=100" 2>/dev/null | jq -r '.[].name' || true
+}
Relevance

●●● Strong

Concrete pagination correctness bug; similar script correctness/reliability fixes are usually
accepted.

PR-#567

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
GitLab label listing is a single request capped at 100, while post-triage relies on the returned
list to decide whether to apply or refuse label_actions.

scripts/gitlab/triage-ops.sh[128-130]
scripts/post-triage.sh[512-545]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
On GitLab, `forge_list_repo_labels` fetches only the first page of labels (100 max). The post-triage label_actions gate uses this as the authoritative label set, so labels beyond page 1 are incorrectly rejected.

## Issue Context
GitHub uses `--paginate` for labels; GitLab needs equivalent pagination (via `page=` and response headers).

## Fix Focus Areas
- scripts/gitlab/triage-ops.sh[128-130]
- scripts/post-triage.sh[512-545]

## What to change
- Implement a pagination loop in `forge_list_repo_labels`, e.g.:
 - loop `page=1..` until an empty array is returned
 - concatenate names across pages
- Alternatively, query label existence by name per action (slower, but avoids full pagination).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. GitLab sticky comment duplicates ✗ Dismissed 🐞 Bug ☼ Reliability
Description
GitLab forge_post_sticky_comment only fetches the first 100 notes sorted ascending when searching
for an existing marker, so on issues with >100 notes it can miss the marker and POST a new note
instead of updating. This can repeatedly spam issues with duplicate “sticky” triage comments on
re-triage.
Code

scripts/gitlab/triage-ops.sh[R158-161]

+  notes=$(_gitlab_api GET "/projects/${REPO}/issues/${ISSUE_NUMBER}/notes?per_page=100&sort=asc" 2>/dev/null) || notes="[]"
+
+  local note_id
+  note_id=$(echo "${notes}" | jq -r --arg marker "${marker}" \
Relevance

●●● Strong

Concrete reliability bug causing duplicate sticky comments; team has accepted fixes preventing
repeated/looping issue mutations.

PR-#326

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The function only requests one page (per_page=100) and uses sort=asc; if the marker note isn’t
in that first page, it will always execute the fallback POST branch, producing duplicates.

scripts/gitlab/triage-ops.sh[150-170]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`forge_post_sticky_comment` queries `notes?per_page=100&sort=asc` once and searches only that response for the marker. If the marker note isn’t in the first page, the script falls back to POSTing a new note, creating duplicates.

## Issue Context
Because results are sorted ascending, page 1 is the oldest notes. A marker note created later (or after many existing notes) may fall outside the first 100 and will never be found.

## Fix Focus Areas
- scripts/gitlab/triage-ops.sh[150-170]

## What to change
- Either:
 - Fetch notes sorted by newest first (`sort=desc`) so the marker is likely on page 1, and still paginate if not found.
- Or:
 - Implement pagination over notes (`page=1..N`) until the marker is found or pages are exhausted.
- Keep the update-in-place behavior by PUTing the found note ID; only POST when you’re sure no marker exists.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (1)
7. Unvalidated forge ops sourcing ✓ Resolved 🐞 Bug ⛨ Security
Description
pre-triage.sh and post-triage.sh source the ops script via an unvalidated FULLSEND_FORGE path, so a
traversal value (e.g., "../..") could cause unintended code execution on the runner if
FULLSEND_FORGE is ever influenced outside the trusted harness. This is a runner trust-boundary
weakness because sourcing executes arbitrary bash from the resolved path.
Code

scripts/post-triage.sh[R22-24]

+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source=/dev/null
+source "${SCRIPT_DIR}/${FULLSEND_FORGE}/triage-ops.sh"
Relevance

●● Moderate

Traversal risk depends on trust boundary for FULLSEND_FORGE; no close allowlist/validation precedent
found.

PR-#415

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Both host scripts construct the sourced file path using ${FULLSEND_FORGE} with no allowlist
validation, so traversal values could resolve outside the intended scripts/{github,gitlab}/
directories if the env var were ever set unexpectedly.

scripts/post-triage.sh[20-25]
scripts/pre-triage.sh[16-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`post-triage.sh` and `pre-triage.sh` build a `source` path from `$FULLSEND_FORGE` without validating it. Although the variable is quoted (preventing shell metacharacter injection), path traversal (e.g., `../..`) can still resolve to an unintended file.

## Issue Context
This code runs on the host/runner (not the sandbox). Today the harness sets `FULLSEND_FORGE` to fixed values, but the scripts should defensively enforce the trust boundary.

## Fix Focus Areas
- scripts/post-triage.sh[20-25]
- scripts/pre-triage.sh[16-21]

## What to change
- Add an explicit allowlist:
 - `case "${FULLSEND_FORGE}" in github|gitlab) ;; *) echo "ERROR: invalid FULLSEND_FORGE"; exit 1 ;; esac`
- Optionally map forge->path without interpolation (e.g., set `OPS_FILE` in the `case` and `source "$OPS_FILE"`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread scripts/pre-triage.sh Outdated
Comment thread policies/gitlab/triage.yaml
Comment thread harness/triage.yaml
Comment thread scripts/post-triage.sh Outdated
Comment thread scripts/gitlab/triage-ops.sh Outdated
Comment thread scripts/lib/gitlab-triage-ops.lib.sh
Comment thread schemas/triage-result.schema.json
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review

Findings

High

  • [protected-path] 20 of 33 changed files are under protected paths (.github/, AGENTS.md, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, AGENTS.md, agents/, harness/, policies/github/, policies/gitlab/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Low

  • [logic-error] scripts/lib/gitlab-triage-ops.lib.sh:119forge_strip_labels issues one PUT /projects/:id/issues/:iid per label instead of batching all labels into a single PUT with comma-separated remove_labels. O(n) API calls instead of O(1), each bumping updated_at. Functional but avoidable performance concern.
  • [logic-error] scripts/lib/gitlab-triage-ops.lib.sh:262forge_close_issue uses _gitlab_api with stdout redirected to /dev/null. On API failure, curl's --show-error writes a generic error to stderr but the function provides no context identifying that issue closure failed specifically. The script does abort under set -e — this is an error-reporting inconsistency, not a functional gap.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:68_gitlab_api_with_status parses HTTP status from the last line of curl output via --write-out '\n%{http_code}'. Parsing is correct in practice since GitLab API JSON responses always end with } or ].
  • [permission-manifest-change] policies/gitlab/triage.yaml — New sandbox policy allows curl as a permitted binary for GitLab API access, whereas the GitHub policy excludes it. Network scope restricted to gitlab.com and gitlab.cee.redhat.com on port 443 only. Symmetric design: GitHub gets gh without curl, GitLab gets curl without gh.
  • [authorization] agents/triage.md:6 — Agent frontmatter adds curl to tool grant (tools: Bash(gh,curl,jq)). On GitHub, mitigated by binary allowlist exclusion in policies/github/triage.yaml. On GitLab, intentionally allowed and constrained by network policy.
  • [credential-exposure] scripts/lib/gitlab-triage-ops.lib.sh:41_gitlab_api passes GITLAB_TOKEN via the PRIVATE-TOKEN header in curl invocations. If set -x is enabled, the token would appear in stderr output. Risk is theoretical — production uses set -euo pipefail without set -x.
  • [schema-breaking-change] schemas/triage-result.schema.jsonprerequisites.create[].repo and sub_issues[].repo patterns expanded from strict 2-segment to multi-segment for GitLab nested groups. Strict superset — existing 2-segment GitHub paths still validate. Downstream parsers assuming exactly one slash need updating.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded via anyOf to accept both GitHub and GitLab URL shapes. Additive — existing GitHub URLs still validate. Downstream parsers that extract repo identifiers must handle both formats.
  • [env-var-rename] env/github/triage.env — Sandbox/runner env var changed from GITHUB_ISSUE_URL to ISSUE_URL. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will need updating. Migration documented in docs/triage.md.
  • [required-env-var] harness/triage.yaml — New required FULLSEND_FORGE env var. Scripts hard-fail if absent. Custom harness overrides that remove forge sections must set this variable explicitly.
  • [skill-path-change] harness/review.yaml:16 — Skill ref changed from skills/issue-labels to skills/issue-labels/github. Downstream base: composition users with old-path overrides will see dedup-by-basename behavior change.
  • [stale-reference] docs/code.md:5 — References "triaged GitHub issues" when the triage agent now supports both GitHub and GitLab. The code agent is GitHub-only, so the reference is technically accurate for its scope, but potentially misleading about the broader triage system.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run

Review

Findings

High

  • [protected-path] 20 of 33 changed files are under protected paths (.github/, AGENTS.md, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, AGENTS.md, agents/, harness/, policies/github/, policies/gitlab/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Low

  • [logic-error] scripts/lib/gitlab-triage-ops.lib.sh:262forge_close_issue uses _gitlab_api with stdout redirected to /dev/null. On API failure, curl's --show-error writes a generic error to stderr but the function provides no context identifying that issue closure failed specifically. The script does abort under set -e — this is an error-reporting inconsistency, not a functional gap.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:121forge_strip_labels batches label removals via comma-separated remove_labels param. Labels containing commas would be misparsed by the GitLab API. Current control labels are safe.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:68_gitlab_api_with_status parses HTTP status from the last line of curl output via --write-out '\n%{http_code}'. Parsing is correct in practice since GitLab API JSON responses always end with } or ].
  • [permission-manifest-change] policies/gitlab/triage.yaml — New sandbox policy allows curl as a permitted binary for GitLab API access, whereas the GitHub policy excludes it. Network scope restricted to gitlab.com and gitlab.cee.redhat.com on port 443. Accepted design trade-off.
  • [authorization] agents/triage.md:6 — Agent frontmatter adds curl to tool grant. On GitHub, mitigated by binary allowlist exclusion in policies/github/triage.yaml. On GitLab, intentionally allowed and constrained by network policy.
  • [credential-exposure] scripts/lib/gitlab-triage-ops.lib.sh:41_gitlab_api passes GITLAB_TOKEN via the PRIVATE-TOKEN header. If set -x is enabled, the token leaks. Risk is theoretical — production does not enable debug tracing.
  • [input-validation] scripts/lib/gitlab-triage-ops.lib.sh:80forge_validate_issue_url host allowlist must stay synchronized with policies/gitlab/triage.yaml endpoints. Currently consistent.
  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
  • [breaking-skill-path] harness/review.yaml:16 — Skill ref changed from skills/issue-labels to skills/issue-labels/github. Downstream base: composition users with old-path overrides will see dedup-by-basename behavior change.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Additive — existing GitHub URLs still validate.
  • [schema-repo-pattern-expansion] schemas/triage-result.schema.jsonprerequisites.create[].repo and sub_issues[].repo patterns expanded from 2-segment to multi-segment for GitLab nested groups. Strict superset of old pattern.
  • [harness-structure-change] harness/triage.yaml — Top-level policy, skills, and host_files moved into forge sections. Downstream base: composition with top-level keys still works per ResolveForge merge semantics.
  • [env-file-path-change] env/github/triage.envenv/triage.env replaced by per-forge files. Downstream repos with explicit host_files references would need updating.
  • [error-handling-idiom] scripts/pre-triage.src.sh — Other .src.sh files guard required env vars at script top with : "${VAR:?message}". pre-triage.src.sh and post-triage.src.sh omit these guards for ISSUE_URL and FULLSEND_FORGE, relying on downstream validation. No functional gap — but idiom diverges from the established pattern.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Findings

High

  • [protected-path] 20 of 33 changed files are under protected paths (.github/, AGENTS.md, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, AGENTS.md, agents/, harness/, policies/github/, policies/gitlab/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Low

  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
  • [breaking-skill-path] harness/review.yaml:16 — Skill ref changed from skills/issue-labels to skills/issue-labels/github. Downstream base: composition users with old-path overrides will see dedup-by-basename behavior change.
  • [permission-manifest-change] policies/gitlab/triage.yaml — New sandbox policy allows curl as a permitted binary for GitLab API access, whereas the GitHub policy excludes it. Network scope restricted to gitlab.com and gitlab.cee.redhat.com on port 443. Accepted design trade-off.
  • [authorization] agents/triage.md:6 — Agent frontmatter adds curl to tool grant. On GitHub, mitigated by binary allowlist exclusion in policies/github/triage.yaml. On GitLab, intentionally allowed and constrained by network policy.
  • [credential-exposure] scripts/lib/gitlab-triage-ops.lib.sh:69_gitlab_api passes GITLAB_TOKEN via the PRIVATE-TOKEN header. If set -x is enabled, the token leaks. Risk is theoretical — production does not enable debug tracing.
  • [input-validation] scripts/lib/gitlab-triage-ops.lib.sh:113forge_validate_issue_url host allowlist must stay synchronized with policies/gitlab/triage.yaml endpoints. Currently consistent.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:152forge_strip_labels batches label removals via comma-separated remove_labels param. Labels containing commas would be misparsed. Current control labels are safe.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Additive — existing GitHub URLs still validate.
  • [schema-repo-pattern-expansion] schemas/triage-result.schema.jsonprerequisites.create[].repo and sub_issues[].repo patterns expanded from 2-segment to multi-segment for GitLab nested groups. Strict superset of old pattern.
  • [harness-structure-change] harness/triage.yaml — Top-level policy, skills, and host_files moved into forge sections. Downstream base: composition with top-level keys still works per ResolveForge merge semantics.
  • [env-file-path-change] env/github/triage.envenv/triage.env replaced by per-forge files. Downstream repos with explicit host_files references would need updating.
  • [code-organization] scripts/lib/triage-ops.lib.sh — New triage-ops lib files omit # shellcheck shell=bash directive present in other bundled lib files. Functional impact is nil (shebang serves the same purpose for shellcheck), but convention is inconsistent.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [logic-error] schemas/triage-result.schema.json:88 — The sub_issues[].repo pattern was not updated to support multi-segment GitLab project paths. prerequisites.create[].repo was correctly updated to ^[a-zA-Z0-9._-]+(/[a-zA-Z0-9._-]+)+$ (accepting group/subgroup/project), but sub_issues[].repo still uses ^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$ (only two segments). A GitLab split action targeting a multi-level namespace would fail schema validation.
    Remediation: Change sub_issues[].repo pattern to ^[a-zA-Z0-9._-]+(/[a-zA-Z0-9._-]+)+$ to match prerequisites.create[].repo.
  • [docs-currency] AGENTS.md:90 — The skill override example in section 7 still shows .agents/skills/issue-labels under a top-level skills: array. This PR moves the upstream skill to skills/issue-labels/github under forge.<platform>.skills. The basename mismatch (issue-labels vs github) means mergeSkills dedup would not replace the upstream skill. docs/triage.md and docs/review.md have been updated but AGENTS.md has not.
    Remediation: Update the example to use .agents/skills/issue-labels/github and describe the per-forge override mechanism.

Low

  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
  • [permission-manifest-change] policies/gitlab/triage.yaml — New sandbox policy allows curl as a permitted binary for GitLab API access, whereas the GitHub policy excludes it. Network scope restricted to gitlab.com and gitlab.cee.redhat.com on port 443. Accepted design trade-off; recommend confirming GITLAB_TOKEN is provisioned with minimum required scopes.
  • [permission-manifest-change] policies/github/triage.yaml:6 — Comment documents that agent frontmatter grants curl but this policy's binary allowlist blocks it. Defense-in-depth is sound; coupling is documented.
  • [authorization] agents/triage.md:6 — Agent frontmatter adds curl to tool grant. On GitHub, mitigated by binary allowlist exclusion. On GitLab, intentionally allowed. Security boundary is enforcement-side (policy), not declaration-side.
  • [code-organization] scripts/lib/github-triage-ops.lib.sh — New lib files omit the include guard pattern ([[ -n "${FOO_SH_LOADED:-}" ]] && return 0) that every other lib file in scripts/lib/ uses. Double-sourcing is unlikely since files are bundled inline, but convention is universal.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:152forge_strip_labels batches label removals via comma-separated remove_labels param. Labels containing commas would be misparsed. Current control labels are safe.
  • [credential-exposure] scripts/lib/gitlab-triage-ops.lib.sh:69_gitlab_api passes GITLAB_TOKEN via the PRIVATE-TOKEN header. If set -x is enabled, the token leaks. Risk is theoretical — production does not enable debug tracing.
  • [input-validation] scripts/lib/gitlab-triage-ops.lib.sh:113forge_validate_issue_url host allowlist must stay synchronized with policies/gitlab/triage.yaml endpoints. Currently consistent.
  • [breaking-review-skill-path] harness/review.yaml:16 — Skill ref changed from skills/issue-labels to skills/issue-labels/github. Downstream base: composition users with old-path overrides will see dedup-by-basename behavior change.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Additive — existing GitHub URLs still validate.
  • [harness-structure-change] harness/triage.yaml — Top-level policy, skills, and host_files moved into forge sections. Downstream base: composition with top-level keys still works per ResolveForge merge semantics.
  • [env-file-path-change] env/github/triage.envenv/triage.env replaced by per-forge files. Downstream repos with explicit host_files references would need updating.
  • [naming-consistency] scripts/lib/triage-ops.lib.sh:7_gha_sanitize naming diverges from established sanitize_* naming. Different scope; divergence is minor.
  • [error-handling-idiom] scripts/lib/github-triage-ops.lib.sh:34forge_add_label uses return 1 vs original exit 1. Appropriate for library code under set -euo pipefail.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (4)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.

Low

  • [stale-reference] AGENTS.md:99 — The skill override example shows .agents/skills/issue-labels as the override path, but the upstream skill basename is now github (from skills/issue-labels/github). The basename mismatch means mergeSkills dedup-by-basename would not replace the upstream skill. Updated guidance is available in docs/triage.md and docs/review.md.
    Remediation: Update the example to use .agents/skills/issue-labels/github.
  • [breaking-review-skill-path] harness/review.yaml:16 — Skill ref changed from skills/issue-labels to skills/issue-labels/github. Downstream repos using base: composition with the old path will see dedup-by-basename behavior change. Custom overrides at .agents/skills/issue-labels/SKILL.md will no longer shadow the upstream skill. See also: [scope-coherence], [code-organization] findings at this location.
  • [scope-coherence] harness/review.yaml:14 — The review harness hardcodes skills/issue-labels/github at the top-level skills: key but has no forge.gitlab section. The review agent has always been GitHub-only — the skill path change is a necessary consequence of the directory restructure.
  • [code-organization] harness/review.yaml:16 — The review harness places skills/issue-labels/github in the top-level skills: array rather than under forge.github.skills, creating an organizational inconsistency with the triage harness pattern. The review agent is currently GitHub-only, so this is functionally correct and expected.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:152forge_strip_labels batches label removals via comma-separated remove_labels param. Labels containing commas would be misparsed by the GitLab API. Current control labels are safe.
  • [credential-exposure] scripts/lib/gitlab-triage-ops.lib.sh:69_gitlab_api passes GITLAB_TOKEN via the PRIVATE-TOKEN header in curl commands. If bash debug mode (set -x) is enabled, the token value could be leaked. Risk is theoretical — production CI runners do not enable debug tracing.
  • [input-validation] scripts/lib/gitlab-triage-ops.lib.sh:113forge_validate_issue_url host allowlist (gitlab.com, gitlab.cee.redhat.com) must stay synchronized with the network policy endpoints in policies/gitlab/triage.yaml. Currently consistent.
  • [gha-workflow-command-injection] scripts/post-triage.src.sh:255FAILED_CREATES variable interpolates TARGET_REPO and ISSUE_TITLE raw into a string used in COMMENT posted as an issue comment. Not a GHA command injection (goes to COMMENT, not stdout). Pre-existing pattern not newly introduced by this diff.
  • [naming-consistency] scripts/lib/triage-ops.lib.sh:7_gha_sanitize naming diverges from established sanitize_* naming in post-failure-report.lib.sh. Different scope, so divergence is minor.
  • [naming-consistency] scripts/lib/gitlab-triage-ops.lib.sh:163 — Error message phrasing differs between GitHub and GitLab forge_add_label implementations. Reflects actual API differences.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Change is additive — existing GitHub URLs still validate. Downstream consumers should handle both formats.
  • [harness-structure-change] harness/triage.yaml — Top-level policy, skills, and host_files moved into forge sections. Downstream harnesses using base: composition that set top-level keys still work per ResolveForge merge semantics.
  • [env-file-path-change] env/github/triage.envenv/triage.env replaced by per-forge files (env/github/triage.env, env/gitlab/triage.env). Downstream repos that explicitly override the host_files entry for the triage env file would need updating.
  • [missing-authorization] This PR modifies 32 files with no linked tracking issue in fullsend-ai/agents. The PR body references upstream dependencies (fullsend-ai/fullsend#5858, #5918) and provides clear rationale, but a tracking issue is recommended for a feature of this scope.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (5)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [stale-example] AGENTS.md:92 — The skill override example shows .agents/skills/issue-labels as the override path, but this PR moved the upstream skill to skills/issue-labels/github. A user following the AGENTS.md example would create an override at a path whose basename (issue-labels) does not match the upstream basename (github), so mergeSkills dedup-by-basename would not replace the upstream skill.
    Remediation: Update the example path from .agents/skills/issue-labels to .agents/skills/issue-labels/github and adjust surrounding text.
  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
    Remediation: Document in release notes. Downstream repos must replace GITHUB_ISSUE_URL with ISSUE_URL and ensure FULLSEND_FORGE is set.

Low

  • [breaking-review-skill-path] harness/review.yaml:16 — Skill ref changed from skills/issue-labels to skills/issue-labels/github. Downstream repos using base: composition with the old path will see dedup-by-basename behavior change (old basename issue-labels, new basename github). Custom overrides at .agents/skills/issue-labels/SKILL.md will no longer shadow the upstream skill. Well-documented in docs/review.md.
  • [gha-workflow-command-injection] scripts/post-triage.src.sh:644echo "Creating prerequisite issue in ${TARGET_REPO}..." outputs TARGET_REPO (from untrusted agent JSON) without _gha_sanitize. The same variable is correctly sanitized in ::warning:: annotations on nearby lines. Schema validation constrains TARGET_REPO to ^[a-zA-Z0-9._-]+(/[a-zA-Z0-9._-]+)+$, making exploitation impractical, but defense-in-depth consistency suggests sanitizing all echo statements that interpolate untrusted values.
  • [gha-workflow-command-injection] scripts/post-triage.src.sh:988echo "Adding label '${LA_LABEL}'..." outputs the untrusted label name without _gha_sanitize. The label has already passed regex validation (^[a-zA-Z0-9._/: +\-]+$) and must exist in the repo (label_exists), making practical exploitation extremely unlikely. Defense-in-depth consistency with nearby sanitized ::warning:: annotations would be an improvement.
  • [credential-exposure] scripts/lib/gitlab-triage-ops.lib.sh:69_gitlab_api passes GITLAB_TOKEN via the PRIVATE-TOKEN header in curl commands. If bash debug mode (set -x) is enabled, the token value could be leaked to stderr/logs. The script uses set -euo pipefail (no -x), and production CI runners do not enable debug tracing. Risk is theoretical and confined to debug scenarios.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:152forge_strip_labels batches label removals via comma-separated remove_labels param. Labels containing commas would be misparsed by the GitLab API. Current control labels are safe.
  • [input-validation] scripts/lib/gitlab-triage-ops.lib.sh:113forge_validate_issue_url host allowlist (gitlab.com, gitlab.cee.redhat.com) must stay synchronized with the network policy endpoints in policies/gitlab/triage.yaml. Currently consistent.
  • [missing-authorization] This PR modifies 32 files with no linked tracking issue in fullsend-ai/agents. The PR body references upstream dependencies (fullsend-ai/fullsend#5858, #5918) and provides clear rationale, but a tracking issue is recommended for a feature of this scope.
  • [scope-coherence] harness/review.yaml:15 — The review harness hardcodes skills/issue-labels/github at top-level skills: key but has no forge.gitlab section. The review agent has always been GitHub-only — this PR does not change that. The skill path change is a necessary consequence of the directory restructure.
  • [schema-repo-pattern-expansion] schemas/triage-result.schema.json:80prerequisites.create[].repo pattern expanded from 2-segment to 2+ segments for GitLab nested group paths. Strict superset of old pattern — 2-segment GitHub paths remain valid. Consumers assuming exactly one slash will need updating.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Change is additive — existing GitHub URLs still validate. Downstream consumers should handle both formats.
  • [harness-structure-change] harness/triage.yaml — Top-level policy, skills, and host_files moved into forge sections. Downstream harnesses using base: composition that set top-level keys still work per ResolveForge merge semantics, but the merge behavior differs by key type.
  • [env-file-path-change] env/github/triage.envenv/triage.env replaced by per-forge files (env/github/triage.env, env/gitlab/triage.env). Downstream repos that explicitly override the host_files entry for the triage env file would need updating.
  • [naming-consistency] scripts/lib/triage-ops.lib.sh:7_gha_sanitize naming diverges from established sanitize_* naming in post-failure-report.lib.sh. Different scope, so divergence is minor.
  • [naming-consistency] scripts/lib/gitlab-triage-ops.lib.sh:163 — Error message phrasing differs between GitHub (POST ${endpoint}) and GitLab (via PUT /path) forge_add_label implementations. Reflects actual API differences.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (6)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [stale-example] AGENTS.md:92 — The skill override example shows .agents/skills/issue-labels as the override path, but this PR moved the upstream skill to skills/issue-labels/github. A user following the AGENTS.md example would create an override at a path whose basename (issue-labels) does not match the upstream basename (github), so the dedup would not replace the upstream skill.
    Remediation: Update the example path from .agents/skills/issue-labels to .agents/skills/issue-labels/github and adjust surrounding text.
  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
    Remediation: Document in release notes. Consider a deprecation shim in env/github/triage.env that also exports GITHUB_ISSUE_URL=$ISSUE_URL for backward compatibility.
  • [breaking-review-skill-path] harness/review.yaml:16 — Skill ref changed from skills/issue-labels to skills/issue-labels/github. Downstream repos using base: composition with the old path will see dedup-by-basename behavior change (old basename issue-labels, new basename github). Custom overrides at .agents/skills/issue-labels/SKILL.md will no longer shadow the upstream skill.
    Remediation: Document in release notes that custom skill overrides must be moved from .agents/skills/issue-labels/SKILL.md to .agents/skills/issue-labels/github/SKILL.md.

Low

  • [credential-exposure] scripts/lib/gitlab-triage-ops.lib.sh:69_gitlab_api passes GITLAB_TOKEN via the PRIVATE-TOKEN header in curl commands. If bash debug mode (set -x) is enabled, the token value could be leaked to stderr/logs. The script uses set -euo pipefail (no -x), and production CI runners do not enable debug tracing. Risk is theoretical and confined to debug scenarios.
  • [scope-coherence] harness/review.yaml:15 — The review harness hardcodes skills/issue-labels/github at top-level skills: key but has no forge.gitlab section. The review agent has always been GitHub-only — this PR does not change that. The skill path change is a necessary consequence of the directory restructure.
  • [error-handling-idiom] scripts/lib/gitlab-triage-ops.lib.sh:118forge_validate_issue_url() emits an error message referencing GITLAB_HOST as the variable name, but GITLAB_HOST is not set until forge_parse_issue_url() is called later. The error uses a local host variable but labels it GITLAB_HOST, which is misleading to operators debugging failures.
  • [naming-consistency] scripts/lib/gitlab-triage-ops.lib.sh:153 — In forge_strip_labels(), the parameter is named labels_to_strip, while in forge_verify_labels_stripped() it is named labels_to_check. The GitHub counterpart uses labels for both functions.
  • [missing-authorization] This PR modifies 32 files with no linked tracking issue in fullsend-ai/agents. The PR body references upstream dependencies (fullsend-ai/fullsend#5858, #5918) and provides clear rationale, but a tracking issue is recommended for a feature of this scope.
  • [schema-repo-pattern-expansion] schemas/triage-result.schema.json:80prerequisites.create[].repo pattern expanded from 2-segment to 2+ segments for GitLab nested group paths. Strict superset of old pattern — 2-segment GitHub paths remain valid. Consumers assuming exactly one slash will need updating.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Change is additive — existing GitHub URLs still validate. Downstream consumers should handle both formats.
  • [harness-structure-change] harness/triage.yaml — Top-level policy, skills, and host_files moved into forge sections. Downstream harnesses using base: composition that set top-level keys still work per ResolveForge merge semantics, but the merge behavior differs by key type.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (7)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [network-policy] policies/gitlab/triage.yaml — The GitLab sandbox policy allows curl as a permitted binary for the gitlab_api network policy, enabling authenticated access to gitlab.com and gitlab.cee.redhat.com. This is architecturally required since the gh CLI is unavailable for GitLab, but grants broader API access capability than the GitHub path (which blocks curl at the binary allowlist level as defense-in-depth).
    Remediation: Document minimum required GitLab token scopes and ensure tokens are scoped to minimum required permissions.
  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
    Remediation: Update custom pre/post scripts to use ISSUE_URL. If overriding harness without forge sections, explicitly set FULLSEND_FORGE=github.

Low

  • [missing-authorization] This PR modifies 32 files with no linked tracking issue in fullsend-ai/agents. The PR body references upstream dependencies (fullsend-ai/fullsend#5858, #5918) and provides clear rationale, but a tracking issue is recommended for a feature of this scope.
  • [logic-error] scripts/lib/gitlab-triage-ops.lib.sh — GitLab forge_add_label/forge_remove_label use PUT /projects/.../issues/... with add_labels/remove_labels, firing issues.updated webhooks per call. The code documents bot detection mitigation (dispatcher's isBotEvent filter), and forge_strip_labels batches removals. Known GitLab API constraint, not a defect.
  • [gha-workflow-command-injection] scripts/lib/triage-ops.lib.sh_gha_sanitize handles the primary injection vectors (:: sequences, % encoding, newline stripping) but does not strip ANSI escape sequences. Used only in lower-risk ::warning::/::notice:: annotations. Risk is limited to cosmetic log manipulation.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Change is additive — existing GitHub URLs still validate. Downstream consumers parsing these URLs should handle both formats.
  • [schema-repo-pattern-expansion] schemas/triage-result.schema.jsonprerequisites.create[].repo pattern expanded to multi-segment for GitLab nested group paths. Strict superset of old pattern — 2-segment GitHub paths remain valid.
  • [logic-error] harness/review.yaml — The review harness places skills/issue-labels/github at the top level rather than under forge.github.skills. Functionally correct (review is GitHub-only), but asymmetric with triage's forge-scoped pattern.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh_gitlab_api_with_status parses HTTP status code using tail -1 with --write-out '\n%{http_code}'. The \n prefix mitigates body/status confusion. Pattern is fragile but adequately mitigated.
  • [test-coverage] scripts/post-triage-test.sh — GitLab test assertions use substring matching on curl logs vs exact gh CLI argument patterns. Error paths are partially exercised (explicit error tests exist for forge_add_label and forge_create_issue, but not for forge_remove_label or forge_close_issue).
  • [error-handling] scripts/lib/github-triage-ops.lib.shforge_create_issue error details flow to stderr; the caller's GHA annotation correctly references (see stderr for details) rather than interpolating the error content.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.shforge_strip_labels joins labels with commas for remove_labels=. Labels containing commas would be misparsed by the GitLab API. Current control labels are safe.
  • [input-validation] scripts/lib/gitlab-triage-ops.lib.shforge_validate_issue_url host allowlist (gitlab.com, gitlab.cee.redhat.com) must stay synchronized with the network policy endpoints in policies/gitlab/triage.yaml. Currently consistent.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (8)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
    Remediation: Update custom pre/post scripts to use ISSUE_URL. If overriding harness without forge sections, explicitly set FULLSEND_FORGE=github.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf in pull_requests[].url and prerequisites.existing[].url. Change is additive — existing GitHub URLs still validate. Downstream consumers that parse these URLs must handle both formats.
  • [schema-repo-pattern-expansion] schemas/triage-result.schema.jsonprerequisites.create[].repo pattern expanded from 2-segment (owner/repo) to multi-segment to support GitLab nested group paths. Backward-compatible but changes the contract for downstream parsers.

Low

  • [logic-error] harness/review.yaml — The review harness hardcodes skills/issue-labels/github at the top level rather than placing it under a forge.github.skills section. The review agent is currently GitHub-only so this works, but creates an asymmetry with triage's forge-scoped pattern.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh_gitlab_api_with_status parses HTTP status code using tail -1 on curl output with --write-out. The \n prefix before the status code mitigates body/status confusion, but the parsing pattern is fragile.
  • [test-coverage] scripts/post-triage-test.sh — GitLab test assertions are weaker than GitHub-side equivalents. run_gitlab_test uses substring matching on curl logs vs exact gh CLI argument patterns. Mock curl always exits 0 for PUT/POST, so forge_add_label and forge_remove_label error paths are not fully exercised.
  • [error-handling] scripts/lib/github-triage-ops.lib.shforge_create_issue captures stderr to err_file. On failure, the caller interpolates $CREATED_URL which is empty. API error details are in stderr but do not reach the GHA annotation.
  • [missing-authorization] This PR modifies 32 files with no linked issue in this repository. The PR body references upstream dependencies but no tracking issue in fullsend-ai/agents.
  • [agent-output-url-formats] agents/triage.md — Triage agent output may now contain GitLab URLs alongside GitHub URLs. Consumers that parse or display URLs from triage output should handle both formats.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (9)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
    Remediation: Update custom pre/post scripts to use ISSUE_URL. If overriding harness without forge sections, explicitly set FULLSEND_FORGE=github.

Low

  • [test-coverage] scripts/post-triage-test.sh — The GitLab test runner run_gitlab_test asserts only that a fixed-string URL path pattern appears in the curl log — a weaker assertion than the GitHub-side tests. The mock curl always exits 0 for PUT/POST, so forge_add_label and forge_remove_label error paths are not exercised.
  • [error-handling] scripts/lib/github-triage-ops.lib.sh:146forge_create_issue captures stderr to err_file and stdout as the return value. On failure, the caller at post-triage.src.sh:680 interpolates $CREATED_URL which is empty because the command substitution failed. API error details are in stderr but do not reach the GHA annotation.
  • [scope-creep] harness/review.yaml:16 — The review agent's issue-labels skill reference was changed from skills/issue-labels to skills/issue-labels/github. Necessary because the skill directory was renamed, but the review agent uses a hardcoded GitHub path at the top level while triage uses forge.<platform>.skills.
  • [error-handling-idiom] scripts/lib/github-triage-ops.lib.sh:62forge_add_label uses return 1 on failure while the previous inline add_label used exit 1. Callers rely on set -euo pipefail to propagate. Functionally equivalent under set -e but a behavioral change from the prior implementation.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Change is additive — existing GitHub URLs still validate.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (10)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.
    Remediation: Update custom pre/post scripts to use ISSUE_URL. If overriding harness without forge sections, explicitly set FULLSEND_FORGE=github.

Low

  • [scope-creep] harness/review.yaml:16 — The review agent's issue-labels skill reference was changed from skills/issue-labels to skills/issue-labels/github. Necessary because the skill directory was renamed, but the review agent uses a hardcoded GitHub path at the top level while triage uses forge.<platform>.skills.
  • [test-coverage] scripts/post-triage-test.sh — The GitLab prerequisites.create test asserts only the URL path pattern — a weaker assertion than the GitHub-side equivalent which checks for the full command pattern. The mock curl always returns 201, so forge_create_issue error path is not tested.
  • [error-handling] scripts/lib/github-triage-ops.lib.sh:117forge_create_issue does not merge stderr into stdout. When creation fails, the caller's warning annotation references $CREATED_URL which will be empty — API error details are lost from the diagnostic message.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:229forge_post_sticky_comment's sed escaping may be incomplete for markers containing BRE metacharacters. Current HTML-comment markers are safe.
  • [credential-exposure-risk] agents/triage.md:9 — Agent frontmatter grants curl for both forges. GitHub policy excludes curl from the binary allowlist. GitLab policy allows curl restricted to specific hosts. PR improves clarity with inline comments explaining the coupling.
  • [breaking-config-structure] harness/triage.yaml — Top-level policy, skills, and host_files keys moved to forge-specific blocks. docs/triage.md notes ResolveForge still supports top-level keys via merge semantics — not a hard break.
  • [schema-url-pattern-expansion] schemas/triage-result.schema.json — URL patterns expanded to accept both GitHub and GitLab formats via anyOf. Change is additive — existing GitHub URLs still validate.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (11)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [scope-creep] harness/review.yaml:16 — The review agent's issue-labels skill reference was changed from skills/issue-labels to skills/issue-labels/github at the top level. The review agent is not the subject of this PR (titled "make triage agent multi-forge"). This creates an inconsistency: triage uses forge-scoped skills under forge.<platform>.skills, while review uses a hardcoded GitHub path at the top level. The change is necessary since the skill directory was moved, but the inconsistent placement warrants attention.
    Remediation: Move the skill reference into forge.github.skills in review.yaml to be consistent with the pattern established by this PR, or acknowledge the inconsistency.
  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.

Low

  • [missing-authorization] This PR modifies 32 files with no linked issue in this repository. The PR body references upstream dependencies (Add Policy to ForgeConfig for per-forge policy selection fullsend#5858, fix(#5917): fetch forge-specific skills and host_files from URL bases fullsend#5918) but no tracking issue in fullsend-ai/agents. A tracking issue is recommended for a feature of this breadth.
  • [scope-creep] scripts/post-triage.sh — The PR converts pre-triage.sh and post-triage.sh to generated-from-source scripts (.src.sh + make script-build). This follows the established pattern used by other agents' scripts and is mechanically required for forge-ops library bundling.
  • [credential-exposure-risk] agents/triage.md:9 — Agent frontmatter grants curl as a tool for both forges. On GitHub, the sandbox policy excludes curl from the binary allowlist, preventing network access. The defense-in-depth is sound, but the implicit coupling between frontmatter grants and policy enforcement could be made more explicit (e.g., a test verifying the GitHub policy excludes curl).
  • [test-coverage] scripts/post-triage-test.sh — The GitLab prerequisites.create test asserts only the URL path pattern — a weaker assertion than the GitHub-side equivalent which checks for the full command pattern including --repo, --title, and --body.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:229forge_post_sticky_comment's sed escaping may be incomplete for markers containing certain regex metacharacters. Current HTML-comment markers are safe.
  • [error-handling] scripts/lib/github-triage-ops.lib.sh:117forge_create_issue uses 2>&1 which mixes stderr into stdout, unlike the GitLab counterpart which separates error output via _gitlab_api_with_status. Functionally correct (caller handles failure).
  • [test-coverage] .github/scripts/select-eval-agents-test.sh:296 — Forge-level tracking tests append duplicate forge: YAML keys via cat >>. YAML duplicate-key behavior is parser-dependent — yq takes the last occurrence, making tests pass, but the fixture state is technically invalid.
  • [breaking-config-structure] harness/triage.yaml — Top-level policy, skills, and host_files keys moved to forge-specific blocks. docs/triage.md notes ResolveForge still supports top-level keys via merge semantics — not a hard break.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (12)

Review

Findings

High

  • [protected-path] 19 of 32 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.

Low

  • [test-coverage] scripts/post-triage-test.sh — The GitLab prerequisites.create test (gitlab-prerequisites-creates-issue) asserts only that the URL path pattern /projects/test-org%2Ftest-project/issues appears in the curl log — a weaker assertion than the GitHub-side equivalent which checks for the full command pattern including --repo, --title, and --body.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:194forge_post_sticky_comment's sed command uses the marker in a regex context with escaping that may be incomplete for markers containing ], ., *, [ etc. Current HTML-comment markers are safe.
  • [stale-reference] .github/scripts/select-eval-agents-test.sh:34 — The setup_fixture references paths renamed in this PR (policies/triage.yaml, env/triage.env, skills/issue-labels). Tests still exercise valid top-level field extraction, but the fixture diverges from the actual harness layout.
  • [error-handling] scripts/lib/github-triage-ops.lib.shforge_create_issue uses 2>&1 which mixes stderr into stdout, unlike the GitLab counterpart which separates error output via _gitlab_api_with_status. Pre-existing pattern extracted from old post-triage.sh; functionally correct (caller handles failure case).
  • [stale-doc] agents/review.md — References GITHUB_ISSUE_URL which is now forge-specific. The review agent is GitHub-only currently, but the naming is inconsistent with the multi-forge triage pattern where the forge-neutral name is ISSUE_URL.
  • [stale-doc] agents/prioritize.md:16 — References GITHUB_ISSUE_URL at lines 16 and 21. While prioritize is GitHub-only currently, the naming is inconsistent with the multi-forge triage pattern.
  • [schema-change-additive] schemas/triage-result.schema.json — URL patterns expanded to anyOf with both GitHub and GitLab patterns. repo pattern relaxed for GitLab nested groups. All changes are additive — existing GitHub-only results continue to validate.
  • [skill-path-change] harness/review.yaml:16 — Skill path changed from skills/issue-labels to skills/issue-labels/github. Downstream base: composition users referencing the old path for dedup may need to update (basename changes from issue-labels to github).
  • [breaking-config-structure] harness/triage.yaml — Top-level policy, skills, and host_files keys moved to forge-specific blocks. docs/triage.md notes ResolveForge still supports top-level keys via merge semantics — not a hard break.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (13)

Review

Findings

High

  • [protected-path] 19 of 31 changed files are under protected paths (.github/, agents/, harness/, policies/, scripts/, skills/). This PR has no linked issue providing authorization for modifying governance and infrastructure files. Human approval is always required for protected-path changes.
    Affected paths: .github/scripts/, agents/, harness/, policies/, scripts/lib/, scripts/post-*, scripts/pre-*, skills/

Medium

  • [docs-currency] docs/network-policy.md:17 — The statement "curl is excluded from every policy to prevent raw HTTP access with injected credentials" is now incorrect. The new policies/gitlab/triage.yaml explicitly includes curl in the binary allowlist for GitLab API access. The blanket claim "every policy" is misleading for operators assessing their security posture.
  • [breaking-env-var] harness/triage.yamlGITHUB_ISSUE_URL renamed to ISSUE_URL in sandbox/runner env. New required FULLSEND_FORGE env var; scripts hard-fail if absent. Custom pre/post scripts referencing GITHUB_ISSUE_URL inside the sandbox will break. Both set automatically by forge blocks. Migration documented in docs/triage.md.

Low

  • [test-coverage] scripts/post-triage-test.sh — The GitLab prerequisites.create test (gitlab-prerequisites-creates-issue) asserts only that POST appears in the curl log — a weak assertion that any POST call would satisfy. The GitHub-side tests assert on the full command pattern. The test exercises the code path but could assert more specifically on the /issues endpoint.
  • [stale-reference] eval/scripts/run-fullsend.sh:175 — Comment says "triage derives them inside its pre-script from ISSUE_URL alone" but the eval script emits GITHUB_ISSUE_URL and the harness maps it to ISSUE_URL. The comment should note the mapping step.
  • [edge-case] scripts/lib/gitlab-triage-ops.lib.sh:194forge_post_sticky_comment's sed command uses the marker in a regex context with only forward-slash escaping. Current markers (HTML comments) are safe, but markers containing ., *, [ etc. would cause misbehavior.
  • [stale-reference] .github/scripts/select-eval-agents-test.sh:34 — The setup_fixture references paths renamed in this PR (policies/triage.yaml, env/triage.env, skills/issue-labels). Tests still exercise valid top-level field extraction, but the fixture diverges from the actual harness layout.
  • [gha-workflow-command-injection] scripts/pre-triage.src.sh:25${ISSUE_URL} interpolated into ::notice:: without _gha_sanitize(). Pre-existing pattern (not a regression); forge_validate_issue_url() regex prevents exploitation in practice.
  • [gha-workflow-command-injection] scripts/post-triage.src.sh:6_gha_sanitize() does not neutralize :: sequences in its output. Current code paths have upstream validation that limits exploitability, but the sanitizer is incomplete for defense-in-depth.
  • [error-handling] scripts/lib/gitlab-triage-ops.lib.sh:250forge_create_issue emits failure message to stdout (no >&2), unlike every other error path in this file. Error text is captured into CREATED_URL instead of appearing as diagnostic output.
  • [docs-currency] README.md:27 — The scripts/ description says "post-scripts (GitHub mutations)" but triage now also performs GitLab mutations. Inconsistent with line 3 which was updated in this PR to say "GitHub and GitLab".
  • [breaking-config-structure] harness/triage.yaml — Top-level policy, skills, and host_files keys moved to forge-specific blocks. docs/triage.md notes ResolveForge still supports top-level keys via merge semantics — not a hard break.
  • [skill-path-change] harness/review.yaml:16 — Skill path changed from skills/issue-labels to skills/issue-labels/github. Downstream base: composition users referencing the old path for dedup may need to update (basename changes from issue-labels to github).
  • [schema-change-additive] schemas/triage-result.schema.json — URL patterns expanded to anyOf with both GitHub and GitLab patterns. repo pattern relaxed for GitLab nested groups. All changes are additive — existing GitHub-only results continue to validate.

Labels: PR modifies sandbox security policies, credential isolation between forges, and workflow command injection handling.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

Comment thread env/gitlab/triage.env

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional finding not anchorable to a line in this diff (file untouched by this PR):

CRITICAL: skill reorg leaves the unrelated review agent's issue-labels skill pointing at a directory with no SKILL.md

harness/review.yaml:16 — This PR moves skills/issue-labels/SKILL.md to skills/issue-labels/github/SKILL.md and adds a sibling skills/issue-labels/gitlab/SKILL.md; skills/issue-labels/ now contains only the github/ and gitlab/ subdirectories, with no SKILL.md directly under it. harness/triage.yaml was correctly updated to the new per-forge paths, but harness/review.yaml (not touched by this PR) still lists the old top-level skills/issue-labels path, and agents/review.md's frontmatter still declares skills: - issue-labels unchanged. The review agent is single-forge/GitHub-only and unrelated to this PR's triage work but shares this skill; after merge, its issue-labels skill mount resolves to a directory with no SKILL.md, silently breaking (or failing to load) its label recommendations.

Suggestion: update harness/review.yaml to reference skills/issue-labels/github (and update agents/review.md's frontmatter to match), or restore a top-level skills/issue-labels/SKILL.md that re-exports/dispatches to the forge-specific variant for backward compatibility. Grep the repo for every consumer of a renamed skill path before merging a skill reorg.

Comment thread scripts/post-triage-test.sh Outdated
Comment thread scripts/gitlab/triage-ops.sh Outdated
Comment thread harness/triage.yaml
Comment thread agents/triage.md
Comment thread scripts/gitlab/triage-ops.sh Outdated
Comment thread schemas/triage-result.schema.json Outdated
@ggallen
ggallen force-pushed the worktree-gitlab-triage-agent branch from e8850dd to 55e87a3 Compare August 10, 2026 20:05
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:07 PM UTC · Completed 8:26 PM UTC

Commit: 55e87a3 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the worktree-gitlab-triage-agent branch from 55e87a3 to a659284 Compare August 10, 2026 20:31
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:32 PM UTC · Completed 8:53 PM UTC

Commit: a659284 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the worktree-gitlab-triage-agent branch from a659284 to 69bea6a Compare August 10, 2026 20:58
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:00 PM UTC · Completed 9:19 PM UTC

Commit: 69bea6a · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added triage-agent enhancement New feature or request labels Aug 10, 2026
@ggallen
ggallen force-pushed the worktree-gitlab-triage-agent branch from 69bea6a to a1283a8 Compare August 10, 2026 21:24
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:26 PM UTC · Completed 9:45 PM UTC

Commit: a1283a8 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

Comment thread scripts/lib/gitlab-triage-ops.lib.sh Outdated
Comment thread scripts/lib/gitlab-triage-ops.lib.sh
Comment thread scripts/lib/gitlab-triage-ops.lib.sh
@ggallen
ggallen force-pushed the worktree-gitlab-triage-agent branch from a1283a8 to a206dd9 Compare August 12, 2026 01:14
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:15 AM UTC · Completed 1:35 AM UTC

Commit: a206dd9 · View workflow run →

@ggallen
ggallen requested review from maruiz93 and waynesun09 August 12, 2026 01:27
fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the worktree-gitlab-triage-agent branch from a206dd9 to a6775b5 Compare August 12, 2026 01:40
Merged via the queue into fullsend-ai:main with commit b98cfb6 Aug 14, 2026
23 of 25 checks passed
@ggallen
ggallen deleted the worktree-gitlab-triage-agent branch August 14, 2026 04:01
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 14, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 4:03 AM UTC · Completed 4:13 AM UTC

Commit: de0a722 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #686 — feat: make triage agent multi-forge (GitHub + GitLab)

Timeline

Date Event
Aug 6 PR opened by ggallen — 33 files, +3110/−303 lines. Large multi-forge feature extracting forge-specific operations into sourced ops files, reorganizing skills/policies/env by platform, making the agent prompt forge-neutral.
Aug 6 qodo-code-review[bot] posts 7 findings (FULLSEND_FORGE path traversal, unsanitized ISSUE_URL in ::notice::, missing pagination). fullsend-ai-review[bot] posts first CHANGES_REQUESTED.
Aug 6 waynesun09 posts first critical finding: skill reorg leaves the review agent's issue-labels skill pointing at a directory with no SKILL.md.
Aug 6–12 fullsend-ai-review[bot] runs 30 CHANGES_REQUESTED reviews, recycling the same ~15 findings across iterations. Early reviews contained valid findings (SSRF, command injection, error-handling asymmetry); later reviews were predominantly noise.
Aug 12 waynesun09 runs multi-agent review squad (Claude ×2, then Claude + Grok). Identifies 9 blocking findings — all HIGH/CRITICAL — that no bot detected.
Aug 13 All 9 blocking findings verified fixed. waynesun09 approves.
Aug 13–14 fullsend-ai-review[bot] continues issuing CHANGES_REQUESTED after human approval.
Aug 14 PR merged by ggallen. Single squash commit.

Review Quality

Human reviewer (waynesun09) found every blocking issue. Bots found none of them.

The 9 HIGH/CRITICAL findings from waynesun09 included: mergeSkills basename collision silently dropping the forge CLI skill, GNU-only sed -i breaking all GitLab test execution, mock pagination handlers causing test suite hangs, URL-encoded REPO vs plain-string comparison always failing on GitLab, sticky-comment spoofing vector missing author check, and documentation claiming fallbacks that were reverted. Each required cross-system reasoning (Go harness code + bash scripts + YAML config) or test infrastructure analysis that the bots could not perform.

The review bot's 30 cycles contained ~25 valid low/medium findings and ~12 false positives or noise findings. Its most problematic behavior: re-raising findings after the author fixed them (the race-condition finding appeared in all 30 reviews, including after the code was rewritten to eliminate the race entirely), and using 6 different tag names for the same conceptual finding (skill-path-change, breaking-skill-path, scope-coherence, scope-creep, etc.).

maruiz93 raised an architectural security observation about credential isolation (GITLAB_TOKEN should use an openshell provider rather than env var injection) that no bot detected — a higher-abstraction-level concern about the sandbox security model.

Proposals Filed

  1. Review agent correctness sub-agent should detect test-infrastructure failures in shell-script test suites — targeting fullsend-ai/agents.

Evidence for Existing Issues (proposals skipped to avoid duplicates)

  • agents#685 (resolve/persist prior-iteration findings on re-review): This PR is a strong case study — the bot re-raised the same race-condition finding in all 30 reviews, even after the code was rewritten to eliminate it. The bot also reused the skill-path-change finding under 6 different tag names across reviews, which would defeat tag-based deduplication.
  • agents#680 (suppress CHANGES_REQUESTED when human approvals satisfy the gate): The bot continued issuing CHANGES_REQUESTED through Aug 13 23:56 UTC, ~12 hours after waynesun09's approval at Aug 13 12:26 UTC.
  • fullsend#2959 (deduplicate findings across re-review iterations): 30 reviews recycling ~15 findings is the most extreme case of this problem observed. The bot generated ~450 total finding instances from ~35 unique findings.
  • agents#314 (verify API endpoint assumptions against external documentation): The bot accepted a false premise that GitLab's REST API requires read-modify-write for labels, flagging the race as "inherent" in every review. waynesun09 identified that GitLab's PUT supports atomic add_labels/remove_labels parameters, eliminating the race entirely.
  • agents#762 (understand harness YAML conventions): The bot could not reason about mergeSkills basename deduplication behavior, missing the HIGH finding that two same-basename skills would collide.

Autonomy Readiness

This PR is a negative signal for review agent autonomy on infrastructure PRs requiring cross-system reasoning. The bot's 30 reviews drove zero code changes; all substantive fixes were driven by human review. The bot's inability to reason about Go harness internals, validate test infrastructure, or verify forge API capabilities means human review remains essential for this class of change.

Proposals filed

ggallen pushed a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit to ggallen/agents that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR fullsend-ai#686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to fullsend-ai#807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit to ggallen/agents that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR fullsend-ai#686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to fullsend-ai#807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit to ggallen/agents that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR fullsend-ai#686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to fullsend-ai#807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit to ggallen/agents that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR fullsend-ai#686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to fullsend-ai#807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit to ggallen/agents that referenced this pull request Aug 15, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR fullsend-ai#686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format

Related to fullsend-ai#807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 17, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format
- GITLAB_HOST derived from ISSUE_URL at runtime, not injected
  via harness env (avoids os.Expand failure on unset var)

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
ggallen added a commit that referenced this pull request Aug 17, 2026
Add forge-dispatch architecture to the code agent, following
the patterns established by the triage agent in PR #686.

New ops libraries:
- scripts/lib/code-ops.lib.sh: forge dispatcher (FULLSEND_FORGE)
- scripts/lib/github-code-ops.lib.sh: GitHub impl using gh CLI
- scripts/lib/gitlab-code-ops.lib.sh: GitLab impl using curl

Refactored scripts:
- pre-code.src.sh: uses forge_* functions for URL validation,
  existing-PR check, label/comment operations
- post-code.src.sh: uses forge_* functions for push auth,
  PR/MR creation, auto-merge, assignee resolution, labels,
  comments, and CI URL generation

Configuration:
- harness/code.yaml: added forge.gitlab section with policy,
  skills, host_files, and env vars
- policies/github/code.yaml: existing policy moved to forge dir
- policies/gitlab/code.yaml: GitLab-specific network policy
  (curl binary, gitlab.com endpoints)
- env/github/code.env: GitHub-specific vars (ISSUE_URL, GH_TOKEN)
- env/gitlab/code.env: GitLab-specific vars (ISSUE_URL,
  GITLAB_TOKEN)
- env/code.env: shared vars only (git identity, timeouts, Go)

Key design decisions:
- PR_NUMBER_FROM_URL convention preserved to avoid SC2153
- forge_list_prs_for_branch propagates errors (fail-closed)
- post-failure-report.lib.sh left untouched (fix agent compat)
- pr-assignee.lib.sh left untouched; GitLab ops remap response
  shapes to match GitHub expected format
- GITLAB_HOST derived from ISSUE_URL at runtime, not injected
  via harness env (avoids os.Expand failure on unset var)

Related to #807

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
@ggallen ggallen mentioned this pull request Aug 21, 2026
32 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants