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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 4 additions & 63 deletions .github/workflows/fullsend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ permissions:

on:
issues:
types: [labeled, opened, edited]
types: [labeled, opened]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[important] This file was modified (4 additions, 63 deletions) when the architectural pattern is to only touch internal/scaffold/ templates and upgrade this repo later via release.

Changes made:

  • Removed edited from on.issues.types
  • Removed issues event branch from dispatch-triage (loses auto-triage on issues: opened)
  • Deleted entire dispatch-prioritize job (61 lines)
  • Updated post-run-link dependencies

These are behavioral changes that go beyond renaming slash commands and aren't mentioned in the PR description. Consider reverting all changes to this file and letting the upgrade happen post-release.

issue_comment:
types: [created]
pull_request_target:
Expand All @@ -45,9 +45,7 @@ jobs:
group: triage-${{ github.event.issue.number || github.event.pull_request.number }}
cancel-in-progress: true
if: >-
(github.event_name == 'issues'
&& (github.event.action == 'opened' || github.event.action == 'edited')) ||
(github.event_name == 'issue_comment' && (
github.event_name == 'issue_comment' && (
github.event.comment.body == '/triage' ||
startsWith(github.event.comment.body, '/triage ') ||
startsWith(github.event.comment.body, format('{0}{1}', '/triage', fromJSON('"\n"'))) ||
Expand All @@ -58,7 +56,7 @@ jobs:
contains(toJSON(github.event.issue.labels.*.name), 'needs-info') &&
!contains(toJSON(github.event.issue.labels.*.name), 'type/feature')
)
))
)
steps:
- name: Build minimal payload
id: payload
Expand Down Expand Up @@ -401,61 +399,6 @@ jobs:
-f source_repo="$SOURCE_REPO" \
-f event_payload="$EVENT_PAYLOAD"

dispatch-prioritize:
runs-on: ubuntu-latest
outputs:
agent: ${{ steps.dispatch.outputs.agent }}
concurrency:
group: prioritize-${{ github.event.issue.number }}
cancel-in-progress: true
if: >-
github.event_name == 'issue_comment'
&& !github.event.issue.pull_request
&& github.event.comment.user.type != 'Bot'
&& (
github.event.comment.body == '/prioritize'
|| startsWith(github.event.comment.body, '/prioritize ')
|| startsWith(github.event.comment.body, format('{0}{1}', '/prioritize', fromJSON('"\n"')))
)
&& (
github.event.comment.author_association == 'OWNER'
|| github.event.comment.author_association == 'MEMBER'
|| github.event.comment.author_association == 'COLLABORATOR'
)
steps:
- name: Build minimal payload
id: payload
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_HTML_URL: ${{ github.event.issue.html_url }}
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
set -euo pipefail
PAYLOAD=$(jq -cn \
--arg in "${ISSUE_NUMBER}" \
--arg iu "${ISSUE_HTML_URL}" \
--arg cb "${COMMENT_BODY:-}" \
'{issue: {number: ($in | tonumber), html_url: $iu},
comment: {body: $cb}}')
echo "json=$PAYLOAD" >> "$GITHUB_OUTPUT"
- name: Dispatch prioritize stage
id: dispatch
env:
GH_TOKEN: ${{ secrets.FULLSEND_DISPATCH_TOKEN }}
EVENT_PAYLOAD: ${{ steps.payload.outputs.json }}
EVENT_TYPE: ${{ github.event_name }}
SOURCE_REPO: ${{ github.repository }}
DISPATCH_REPO: ${{ github.repository_owner }}/.fullsend
run: |
DISPATCH_URL=$(gh workflow run dispatch.yml \
--repo "$DISPATCH_REPO" \
-f stage=prioritize \
-f event_type="$EVENT_TYPE" \
-f source_repo="$SOURCE_REPO" \
-f event_payload="$EVENT_PAYLOAD")
echo "::notice::Dispatched prioritize → ${DISPATCH_URL}"
echo "agent=prioritize" >> "$GITHUB_OUTPUT"

dispatch-gh-classify:
runs-on: ubuntu-latest
if: >-
Expand Down Expand Up @@ -539,7 +482,6 @@ jobs:
dispatch-review,
dispatch-fix-bot,
dispatch-fix-human,
dispatch-prioritize,
]
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -551,8 +493,7 @@ jobs:
|| needs.dispatch-code.outputs.agent
|| needs.dispatch-review.outputs.agent
|| needs.dispatch-fix-bot.outputs.agent
|| needs.dispatch-fix-human.outputs.agent
|| needs.dispatch-prioritize.outputs.agent }}
|| needs.dispatch-fix-human.outputs.agent }}
SHIM_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
ITEM_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
run: |
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ ADR 0002: [Building block 1](ADRs/0002-initial-fullsend-design.md#1-webhook--dis

### 2. Slash-command parser + ACL

Parses `/triage`, `/code`, `/review`, and related commands and enforces who is allowed to invoke each.
Parses `/fs-triage`, `/fs-code`, `/fs-review`, and related commands and enforces who is allowed to invoke each.
ADR 0002: [Building block 2](ADRs/0002-initial-fullsend-design.md#2-slash-command-parser--acl).

### 3. Label state machine guard
Expand Down Expand Up @@ -273,7 +273,7 @@ ADR 0002: [Building block 13](ADRs/0002-initial-fullsend-design.md#13-observabil

### 14. retro agent runtime

Retrospective analyst — examines completed or in-progress agent workflows, identifies improvement opportunities, and files proposals as GitHub issues. Runs automatically on PR close (merged or rejected) and on-demand via `/retro` command. Analyzes the full workflow graph (triage, code, review, fix agent interactions and human interventions) and posts a summary comment on the originating PR/issue linking to all filed proposals.
Retrospective analyst — examines completed or in-progress agent workflows, identifies improvement opportunities, and files proposals as GitHub issues. Runs automatically on PR close (merged or rejected) and on-demand via `/fs-retro` command. Analyzes the full workflow graph (triage, code, review, fix agent interactions and human interventions) and posts a summary comment on the originating PR/issue linking to all filed proposals.

## Configuration layering

Expand Down
2 changes: 1 addition & 1 deletion docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ See [ADR 0002](ADRs/0002-initial-fullsend-design.md).

### Slash Command

A GitHub comment in the form `/triage`, `/code`, `/review`, etc., that manually triggers an agent workflow. Slash commands are parsed by the entry point and gated by an ACL — not every user can invoke every command. They provide an explicit human-initiated trigger alongside the automatic label-based triggers.
A GitHub comment in the form `/fs-triage`, `/fs-code`, `/fs-review`, `/fs-fix`, etc., that manually triggers an agent workflow. The `/fs-` prefix avoids collisions with other AI tools that respond to generic command names like `/review`, and the hyphen separator (rather than a space) prevents ambiguity when appending instructions (e.g., `/fs-fix add error handling`). Slash commands are parsed by the entry point and gated by an ACL — not every user can invoke every command. They provide an explicit human-initiated trigger alongside the automatic label-based triggers.
See [ADR 0002](ADRs/0002-initial-fullsend-design.md) building block 2.

## T
Expand Down
31 changes: 17 additions & 14 deletions docs/guides/user/bugfix-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The triage agent reads the issue title, body, comments, and GitHub-native attach
- Put key information in the issue body — expected behavior, actual behavior, steps to reproduce, version/environment.
- Use GitHub's native file attachments for logs, screenshots, or reproduction scripts.
- You can add details via comments — the triage agent reads those too. Other users can also comment with additional context (e.g., confirming the bug on a different platform).
- Editing the issue title or body triggers triage automatically. You can also use `/triage` to force a fresh run.
- Editing the issue title or body triggers triage automatically. You can also use `/fs-triage` to force a fresh run.

### Labels are the state machine

Expand All @@ -58,10 +58,12 @@ You can control the pipeline from issue or PR comments:

| Command | Where | Effect |
|---------|-------|--------|
| `/triage` | Issue comment | Re-runs triage from scratch (clears all labels, reopens if closed) |
| `/code` | Issue comment | Hands off to the code agent (expects `ready-to-code` or forces with human ack) |
| `/review` | PR comment | Enqueues a new review round for the current PR head |
| `/retro` | Issue or PR comment | Triggers a retrospective analysis of the workflow |
| `/fs-triage` | Issue comment | Re-runs triage from scratch (clears all labels, reopens if closed) |
| `/fs-code` | Issue comment | Hands off to the code agent (expects `ready-to-code` or forces with human ack) |
| `/fs-review` | PR comment | Enqueues a new review round for the current PR head |
| `/fs-fix` | PR comment | Requests the fix agent to address review feedback (OWNER/MEMBER/COLLABORATOR only) |
| `/fs-retro` | Issue or PR comment | Triggers a retrospective analysis of the workflow |
| `/fs-stop-fix` | PR comment | Disables bot-triggered fix agent runs on this PR |

### What to expect from agent PRs

Expand Down Expand Up @@ -97,7 +99,7 @@ Every push to a PR in the review stage triggers a new review round. This means `

### Stage 1: Triage

**Triggered by:** issue creation, issue title/body edit, or `/triage` command.
**Triggered by:** issue creation, issue title/body edit, or `/fs-triage` command.

The triage agent:

Expand All @@ -108,11 +110,11 @@ The triage agent:
5. **Produces a test artifact.** When possible, writes a failing test case aligned with the repo's test framework.
6. **Hands off.** Labels `ready-to-code` with a summary comment.

**If triage gets it wrong:** Add a comment with the missing information, or edit the issue body. Edits to the title or body trigger triage automatically. You can also use `/triage` to force a fresh run — this clears all previous labels and starts from scratch.
**If triage gets it wrong:** Add a comment with the missing information, or edit the issue body. Edits to the title or body trigger triage automatically. You can also use `/fs-triage` to force a fresh run — this clears all previous labels and starts from scratch.

### Stage 2: Code

**Triggered by:** `ready-to-code` label or `/code` command.
**Triggered by:** `ready-to-code` label or `/fs-code` command.

The code agent:

Expand All @@ -125,7 +127,7 @@ The code agent:

### Stage 3: Review

**Triggered by:** `pull_request_target` events (PR opened, push to PR branch, or marked ready for review), `/review` command, or `ready-for-review` label.
**Triggered by:** `pull_request_target` events (PR opened, push to PR branch, or marked ready for review), `/fs-review` command, or `ready-for-review` label.

The review swarm:

Expand All @@ -139,20 +141,21 @@ Re-review happens automatically on every push to the PR. The `ready-for-merge` l

Once the PR is merged (by human, merge queue, or automation per org governance), the automated pipeline for this issue is complete.

The **retro agent** ([#131](https://github.com/fullsend-ai/fullsend/issues/131)) runs automatically when a PR is closed (merged or rejected) and can also be triggered on-demand via `/retro` on any issue or PR comment. It analyzes the full workflow graph — triage, code, review, and fix agent interactions plus any human interventions — to identify improvement opportunities. Proposals are filed as GitHub issues in the appropriate repo, and a summary comment is posted on the originating PR/issue linking to all proposals.
The **retro agent** ([#131](https://github.com/fullsend-ai/fullsend/issues/131)) runs automatically when a PR is closed (merged or rejected) and can also be triggered on-demand via `/fs-retro` on any issue or PR comment. It analyzes the full workflow graph — triage, code, review, and fix agent interactions plus any human interventions — to identify improvement opportunities. Proposals are filed as GitHub issues in the appropriate repo, and a summary comment is posted on the originating PR/issue linking to all proposals.

## Intervening in the pipeline

### Stopping automation

- Remove the triggering label (`ready-to-code`) to prevent the next stage from starting. Note: review is triggered automatically by PR events (`pull_request_target`), so closing the PR is the way to stop review dispatch.
- Close the issue. Agents don't act on closed issues (except `/triage` which explicitly reopens).
- Close the issue. Agents don't act on closed issues (except `/fs-triage` which explicitly reopens).

### Restarting a stage

- `/triage` — wipes all labels, reopens the issue, runs triage fresh.
- `/code` — restarts the code agent from the current issue state.
- `/review` — enqueues a new review round.
- `/fs-triage` — wipes all labels, reopens the issue, runs triage fresh.
- `/fs-code` — restarts the code agent from the current issue state.
- `/fs-review` — enqueues a new review round.
- `/fs-fix` — requests the fix agent to address review feedback.

### Taking over manually

Expand Down
8 changes: 4 additions & 4 deletions docs/normative/admin-install/v1/adr-0013-enrollment/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The shim file MUST match the content embedded in `internal/scaffold/fullsend-rep

1. **Event payload via environment variables** — All GitHub Actions context values (`toJSON(github.event)`, `github.event_name`, `github.repository`, `github.repository_owner`) are assigned to environment variables (`EVENT_PAYLOAD`, `EVENT_TYPE`, `SOURCE_REPO`, `DISPATCH_REPO`) and referenced as `"$VAR"` in the `run:` script. This prevents script injection from attacker-controlled fields (issue titles, comment bodies, PR descriptions).

2. **Slash-command matching** — Uses a three-condition pattern per command: (1) exact equality for bare commands (`github.event.comment.body == '/triage'`), (2) `startsWith` with a trailing space for same-line arguments (`startsWith(github.event.comment.body, '/triage ')`), and (3) `startsWith` with a trailing newline for multi-line bodies (`startsWith(github.event.comment.body, format('{0}{1}', '/triage', fromJSON('"\n"')))`). The `fromJSON('"\n"')` expression produces a literal newline character, which GitHub Actions expression strings cannot represent directly. This avoids false positives from partial matches like `/triagefoo` while supporting both inline arguments and newline-separated bodies.
2. **Slash-command matching** — Uses a three-condition pattern per command: (1) exact equality for bare commands (`github.event.comment.body == '/fs-triage'`), (2) `startsWith` with a trailing space for same-line arguments (`startsWith(github.event.comment.body, '/fs-triage ')`), and (3) `startsWith` with a trailing newline for multi-line bodies (`startsWith(github.event.comment.body, format('{0}{1}', '/fs-triage', fromJSON('"\n"')))`). The `fromJSON('"\n"')` expression produces a literal newline character, which GitHub Actions expression strings cannot represent directly. This avoids false positives from partial matches like `/fs-triagefoo` while supporting both inline arguments and newline-separated bodies.

3. **Label matching** — Uses `github.event.action == 'labeled' && github.event.label.name == 'ready-to-code'` (exact match on the triggering label) rather than `contains(github.event.issue.labels.*.name, ...)` (which scans all labels). This ensures dispatch fires only on the labeling action that matters, not on unrelated label additions to an issue that already has the label.

Expand Down Expand Up @@ -83,9 +83,9 @@ jobs:
if: >-
github.event_name == 'issues' ||
(github.event_name == 'issue_comment' && (
github.event.comment.body == '/triage' ||
startsWith(github.event.comment.body, '/triage ') ||
startsWith(github.event.comment.body, format('{0}{1}', '/triage', fromJSON('"\n"')))
github.event.comment.body == '/fs-triage' ||
startsWith(github.event.comment.body, '/fs-triage ') ||
startsWith(github.event.comment.body, format('{0}{1}', '/fs-triage', fromJSON('"\n"')))
))
steps:
- name: Dispatch triage
Expand Down
2 changes: 1 addition & 1 deletion docs/problems/security-threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ An attacker triggers excessive consumption of compute, API tokens, or event-proc

**Event flooding:**
- Rapidly filing issues, posting comments, toggling labels, or creating PRs to trigger agent invocations at scale
- Abusing slash commands (`/triage`, `/code`) to queue expensive operations
- Abusing slash commands (`/fs-triage`, `/fs-code`) to queue expensive operations
- Creating issues in bulk across multiple repos in an organization to saturate shared infrastructure

**Cost amplification:**
Expand Down
Loading
Loading