Skip to content
Back to skills

Contributing

BSecurity

The pull request loop for this repo: branch → commit → verify in your own box (local tests + local stack) → PR into main → demo video recorded with agent-browser on the local stack → `gh --attach` → self-merge → verify on dev. A PR into main runs no CI unless a person adds the `test` or `preview` label (one run each). Load when opening, updating, or finishing a pull request; when writing a PR body; when asking what CI runs where; when adding or explaining PR labels or the per-PR preview envir...

  • 20,239 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
ai-agentsgobashterraformtestinggitapifrontendsecurity

Works with

  • cursor
  • terminal
  • cli
  • api

Security analysis

B88/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 8 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add kortix-ai/suna --skill contributing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Contributing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Contributing
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/kortix-ai-contributing/badge)](https://www.skillsdirectory.com/skills/kortix-ai-contributing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: contributing
description: "The pull request loop for this repo: branch → commit → verify in your own box (local tests + local stack) → PR into main → demo video recorded with agent-browser on the local stack → `gh --attach` → self-merge → verify on dev. A PR into main runs no CI unless a person adds the `test` or `preview` label (one run each). Load when opening, updating, or finishing a pull request; when writing a PR body; when asking what CI runs where; when adding or explaining PR labels or the per-PR preview environment; or when attaching an image or video to a PR, issue, or comment."
---

# Contributing: the pull request loop

Every change reaches `main` through a pull request. A pull request into `main` runs **no**
GitHub Actions job: your development machine runs every test, the stack, and the demo before
the PR opens, and the PR is mergeable at once. Each PR carries a demo video of the change,
recorded with **agent-browser** against your local stack. The video is uploaded with `gh --attach` and appears
in the PR body. This skill is that loop, end to end.

`AGENTS.md` owns the policy: canonical branches, when to self-merge, and the customer-data
rule. This skill is the procedure. Read `AGENTS.md` → "First, at session start" and
"Default delivery" before step 1 if you have not.

## Preflight (once per machine)

```bash
gh --version                 # ≥ 2.99.0: first release with --attach
agent-browser --version      # installed: npm i -g agent-browser && agent-browser install
agent-browser doctor         # "Recording" must pass: ffmpeg with libvpx + libx264
gh auth status               # token prefix gho_, ghp_, or github_pat_ (see attachments reference)
```

Done when all four pass. A `ghs_` or `ghu_` token cannot attach. See
[references/attachments.md](references/attachments.md) → "Token types".

## Steps

### 1. Branch and worktree

Join the canonical branch for the work, or create one:
`pnpm worktree create --name <slug> --yes --no-start` (the **worktree** skill). All edits and
runs happen under `../suna-<slug>`.

Done when `git branch --show-current` prints the canonical branch inside its worktree.

### 2. Commit

- Use the Conventional Commits subject style that `git log` shows:
  `fix(sandbox): …`, `feat(web): …`, `refactor(api): …`, `docs(repo): …`.
- `pnpm install` arms `.githooks`. The hooks encrypt staged `.env` files, block plaintext
  secrets, refuse blocked customer terms, and refuse a crash dump or a file over 20 MB. When a
  hook fires, fix the content and commit again. Keep the hooks on every commit (never
  `--no-verify`).
- Stage files by name: `git add <path> <path>`. Never `git add -A`, `git add .`, or
  `git commit -a` outside one named directory. A blanket add once committed a worker's core
  dump, and a core dump holds every secret in the process environment.
- Ship the tests with the behaviour change (the **testing** skill).

Done when the commit exists and the hooks passed.

### 3. Verify in your box

Run the narrowest relevant test first, then `pnpm test` (the **testing** skill). Then run
the changed behaviour on your worktree's stack: `pnpm worktree start <slug>` prints the web
and API ports. Exercise the real surface: the HTTP route with `curl`, the real CLI process,
or the page with agent-browser. No CI lane runs these for you before the merge.
`pnpm test` writes `tests/attestations/<branch>.json` and deletes every other file
there: commit `tests/attestations/` (`git add -A tests/attestations`). If a merge of
`origin/main` conflicts on the legacy `tests/test-attestation.json`, delete it. The pre-push hook and the
merge gate run `pnpm test:verify` against the pushed head and reject a stale or red
attestation. Never push with `--no-verify`.

Done when the commands you will list under "How was this tested?" passed, with output
captured.

### 4. Record the demo video on the local stack

The demo is a short video of the changed behaviour on a real surface: your worktree's web
app (`http://localhost:<web port>`). Run it as a bash script from the repo root. zsh does
not word-split, so a command stored in a variable fails there.

```bash
#!/usr/bin/env bash
set -euo pipefail
S=http://localhost:<web port>        # from `pnpm worktree start <slug>` or `pnpm worktree ls`
SESSION=$(agent-browser session id --scope worktree --prefix pr-demo)
ab() { agent-browser --session "$SESSION" "$@"; }

# Sign in before recording, so the video never shows an auth form.
.agents/skills/contributing/scripts/preview-sign-in.sh "$S" "$SESSION"   # prints the synthetic email

# Absolute paths only: the agent-browser daemon is shared by every session on
# the machine and resolves a relative path against the cwd of whichever
# session started it, which can be another worktree.
OUT="$PWD/output/pr"
mkdir -p "$OUT"
ab set viewport 1440 900
ab open "$S/<changed route>"
ab wait --load networkidle          # record a rendered page, not a hydrating one
ab record start "$OUT/demo.mp4" --cursor
#   Drive the change: `ab snapshot -i`, then `ab click @eN`, `ab fill @eN …`.
#   Put `ab wait 800` between actions so a person can follow.
ab record stop
ab close
```

`preview-sign-in.sh` creates `pr-demo-<epoch>@example.test` and requests the sign-in email.
On a local origin it reads the link or code from local Supabase's Mailpit
(`127.0.0.1:54324`), and it waits until the browser leaves `/auth`. Load
`agent-browser skills get core` for the full command set.

Rules for the video:

- Show the change, from the starting state to the visible result, in under 60 s. One flow
  per video. Use more videos for more flows.
- Use synthetic data only (`@example.test` emails, invented names). The repo is public, and
  every attachment URL is public.
- `.mp4` plays in every browser. Keep each file under 100 MB (`ls -lh output/pr/`).
- For a change with no UI (API, CLI, infra), record the terminal output or the rendered
  result on GitHub. For example, open the changed file on the branch at
  `https://github.com/kortix-ai/suna/blob/<branch>/<path>`. Or state in the PR why a video
  adds nothing.

Look at the video before you attach it. Extract four frames and read them:

```bash
for t in 1 5 10 15; do ffmpeg -v error -y -ss $t -i output/pr/demo.mp4 -frames:v 1 -vf scale=720:-1 output/pr/frame-$t.png; done
```

Blank frames mean the page had not rendered. An oversized pointer means the page's CSS
broke the `--cursor` overlay: record that page without `--cursor`.

Done when the frames show the change from start to result, with only synthetic data.

### 5. Open the PR

```bash
git push -u origin HEAD
gh pr create --base main \
  --title "<type>(<scope>): <what changed>" --body-file output/pr/body.md \
  --attach ./output/pr/demo.mp4
```

- Build `body.md` from `.github/pull_request_template.md`, with every section filled. The
  line `![Demo](./output/pr/demo.mp4)` holds the video; `--attach` uploads it and rewrites
  the path in one step, so skip step 6.
- Put `body.md` and the recordings in the gitignored `output/pr/` directory. Keep them out
  of tracked paths.
- Open it as a draft (`--draft`) only when the work is not finished. A verified change goes
  straight to review-ready.
- The PR runs no CI job. Add `test` or `preview` only when you need that one explicit run. See "What runs where" and "Labels" below.

Done when `gh pr view --json url` prints the PR.

### 6. Attach the video to the PR

```bash
# body.md contains the line:  ![Demo](./output/pr/demo.mp4)
gh pr edit <pr> --body-file output/pr/body.md --attach ./output/pr/demo.mp4
```

`gh` uploads the file to GitHub and replaces the local reference in the body with the
uploaded URL. The URL renders as a video player. Run the command from the directory the
body's relative paths resolve from: the repo root. The full rules and failure modes are in
[references/attachments.md](references/attachments.md).

Done when the body has the asset URL and no local link is left:

```bash
gh pr view <pr> --json body --jq .body | grep -oE 'https://github.com/user-attachments/assets/[0-9a-f-]+'  # ≥ 1 URL
gh pr view <pr> --json body --jq .body | grep -cE '\]\(\./output/'                                        # 0
```

### 7. Keep it current

- Keep the PR mergeable: `gh pr view <pr> --json mergeable` must not say `CONFLICTING`.
  Merge `main` into the branch and push.
- When the behaviour in the video changes, record the video again and repeat step 6.
- Edit the body after an upload from the live copy:
  `gh pr view <pr> --json body --jq .body > output/pr/body.md`. The old local file still holds
  `./output/pr/demo.mp4`. `--body-file` without `--attach` would publish that path as a broken
  link.
- Merge `main` into the branch daily. Git's rename detection carries `main`'s edits
  through moved files. GitHub's conflict check does not, so push the merge.

### 8. Merge and hand off

- Self-merge when the change is verified (`AGENTS.md` → "Default delivery", rule 5): the
  local checks passed and the PR is mergeable. Do not wait for the user's approval, and do
  not wait for a CI check: none runs. `gh pr merge <pr> --squash`.
- A push to `main` does not deploy dev and does not run `Tests`. Deploy deliberately:
  `gh workflow run deploy-dev.yml -f surface=changed` (`changed` ships every merge since
  dev's live SHA; `all` forces every surface; `frontend` builds the web app only). Follow the
  run to the "Live on dev" comment, then verify the change on dev.
- Report the PR URL, the merge SHA, the local test commands and their results, the dev
  verification, and anything still unverified.
- Merging into `staging` or `prod`, and every release step, still needs the user's explicit
  approval (the **kortix-release** skill).

## What runs where

| Event | Workflows | Blocks? |
| --- | --- | --- |
| PR into `main` | none. Adding `test` runs the six `Tests` lanes once (~9 min); adding `preview` deploys once (~7 min), with no tests. A push re-runs neither. | no |
| Push to `main` (the merge) | `secret-scan`, `secrets-guard`, path-gated `DB Migrations`, `i18n-catalogs`, `deploy-api-router-dev`, `Terraform Apply Global`. Nothing else. | no |
| Dispatch / schedule on `main` | `Deploy Dev` and `Desktop`: dispatch only. `Tests`: daily. `drata`: daily. `CI`, `CodeQL`: weekly. | no |
| PR into `staging` | the six `Tests` lanes, `CI`, `CodeQL`, `secret-scan`, `secrets-guard`, path-gated `DB Migrations`, `Terraform CI`, `Security Scan`, `i18n-catalogs`, `drata` | release discipline |
| PR into `prod` | the same scanners plus `tests-release.yml`; its `full suite + quality gates` check is the only required check in the repo | yes |

`tests/unit/sandbox-workflow.test.ts` fails when a workflow other than the label-gated
`tests.yml` and `deploy-preview.yml` triggers on a pull request into `main`. Move a new check to a schedule, a dispatch, or the release
PRs, never to PRs into `main`. Add it to `push: main` only when it takes seconds: a
push runs on GitHub-billed minutes, and the factory merges ~37 PRs a day.

## Labels

| Label | Effect | Who can add it |
| --- | --- | --- |
| `test` | Runs the six `Tests` lanes (~9 min) once, on the head SHA when the label is added. A push does not re-run it; remove and re-add the label to run again. | Triage access. |
| `preview` | Builds one self-host environment for the branch on Platinum (~7 min), once, and runs no tests. A push does not redeploy; re-add the label. Removing the label tears it down. `gh workflow run deploy-preview.yml -f pr_number=<N>` redeploys and runs `pnpm test -- --target-full` (40–80 min). See [references/preview-environments.md](references/preview-environments.md). | Needs write access, and a PR from a branch of this repo (not a fork). |
| `i18n-reorder` | Lets `i18n-catalogs.yml` accept an intentional key reorder in `apps/web/translations/*.json` on a release PR. On `main`, the same reorder needs `I18N_REORDER=1` past `.githooks/pre-commit` and the commit trailer `I18n-Reorder: intentional`. | Triage access. |

Both labels are explicit, rare requests. Never add one by default, from a template, or from
automation.

Files in this skill

  • SKILL.md10.7 KB
  • references/attachments.md3.5 KB
  • references/preview-environments.md5.8 KB
  • scripts/preview-auth-email.sh1.9 KB
  • scripts/preview-origin.sh3.5 KB
  • scripts/preview-sign-in.sh2.6 KB
  • scripts/preview-subscribe.sh4.6 KB
  • scripts/preview-subscribe.ts4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…