pr-creation

SkillDev tools

Use whenever you are about to create, push, open, update, or edit a pull request in the Handsontable monorepo — load this BEFORE running `gh pr create` or pushing a feature/docs/fix branch, not only when the user says "PR". Triggers: create/open/submit a PR, push the branch, commit and push, ship it, ready to review, update or fix a PR description, any PR URL, finishing work on a `feature/*`/`docs/*`/`fix/*` branch, or a completed ClickUp/DEV task ready to submit. Covers branch naming, pre-flight lint/tests, the PR-first then changelog flow, `gh auth` fallback, and filling the GitHub PR template.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the pr-creation skill

What this skill tells your AI

The instructions your AI receives, as published by handsontable/handsontable in .claude/skills/pr-creation/SKILL.md and read by ahel’s review.

1. Branch Naming

Choose the prefix that matches your work:

TypePatternExample
Feature (ClickUp, default)feature/<TASK-ID>_Short-Descriptionfeature/DEV-627_Forum-Update
Docs (ClickUp)docs/<TASK-ID>_Short-Descriptiondocs/DEV-458_Clarify-undo-redo-docs
Feature (public GitHub issue)feature/issue-xxxxfeature/issue-11832
Docs (public GitHub issue)docs/issue-xxxxdocs/issue-9500
Releaserelease/x.y.zrelease/16.1.0

<TASK-ID> is the ClickUp custom ID. Its prefix follows the space the task lives in, so it is not always DEV: docs/SU-833_BeforeKeyDown-Return-False-Note and feature/PRO-858_Theme-API-e2e-test-data-driven-for-each-theme are both valid. Copy the prefix from the task, never assume one.

When working from a ClickUp task, the human-readable custom ID (e.g. DEV-627, IT-42) must appear in the branch name so ClickUp links automatically. Never use the internal ClickUp hash ID (e.g. 86c9j4fxj) — it is not a valid task identifier for branch linking.

Important: clickup_create_task returns custom_id: null in its response. Always call clickup_get_task immediately after creating a task to retrieve the real custom ID before naming the branch:

1. clickup_create_task → returns task_id (hash, e.g. "86c9j4fxj")
2. clickup_get_task(task_id) → returns custom_id (e.g. "DEV-1532")
3. Use custom_id in the branch name: feature/DEV-1532_Short-Description

2. Pre-flight Checks

Run these before opening the PR. Fix any failures first.

# Lint
npm run eslint --prefix handsontable
npm run stylelint --prefix handsontable

# Build (wrappers depend on this output)
npm run build --prefix handsontable

# Unit tests for the area you changed
npm run test:unit --prefix handsontable --testPathPattern=<regex>

# E2E tests for the area you changed
npm run test:e2e --prefix handsontable --testPathPattern=<regex>

# If you touched a wrapper, test it too
npm run test --prefix wrappers/react-wrapper
npm run test --prefix wrappers/vue3
npm run test --prefix wrappers/angular-wrapper

3. Fill the PR Template

The repository has a PR template at .github/PULL_REQUEST_TEMPLATE.md. Fill in each section:

  • Context -- Explain why the change is needed, not just what changed. Link the ClickUp task or GitHub issue.
  • How has this been tested? -- List the specific tests you added or ran (unit, E2E, manual). Include commands someone can copy-paste to reproduce.
  • Types of changes -- Check the box that applies: bug fix, new feature, breaking change, or translation.
  • Related issue(s) -- Link GitHub issues with #xxx. Include ClickUp task IDs (e.g. DEV-627) so they auto-link.
  • Affected project(s) -- Check every package your change touches: handsontable, @handsontable/react-wrapper, @handsontable/angular-wrapper, @handsontable/vue3.
  • Checklist -- Confirm code style, CLA signature, and whether documentation needs updating. The CLA is checked automatically by the required cla/signed status check; one signature covers Handsontable and HyperFormula. See CONTRIBUTING.md.

4. Target Branch

All PRs target the develop branch. Cherry-picks to release/* or lts/* branches are handled separately by maintainers.

5. Create the PR First (before the changelog)

The PR is created before the changelog entry. The changelog file is named after the PR number, so you need the number the GitHub API returns from gh pr create before you can write the file correctly. Creating the changelog first and guessing the next PR number is unreliable — other PRs can be opened between your check and your push.

Workflow:

  1. Commit the code change on the feature branch.
  2. Push the branch (use gh auth setup-git once if git is configured for SSH and the SSH key is unavailable in the session; then push over HTTPS).
  3. Run gh pr create and capture the returned PR URL / number.
  4. Use that PR number when creating the changelog entry (next step).

Use the GitHub CLI to create the PR. Always create PRs as drafts -- the author marks it ready for review when appropriate.

Authentication fallback: If git push fails with Permission denied (publickey) because the session has no SSH key, switch the remote to HTTPS and let gh provide credentials, then push:

git remote set-url origin https://github.com/handsontable/handsontable.git
gh auth setup-git
git push -u origin <branch-name>

Always write the PR body to a temp file and use --body-file. Never use --body "$(cat <<'EOF'...EOF)" — backticks inside a heredoc passed through shell command substitution are stored as literal ``` characters in GitHub, breaking all inline code formatting in the PR description.

Use a unique, task-scoped temp filename — not a fixed /tmp/pr-body.md. The Write tool refuses to overwrite a file it has not read in the current session, so a stale /tmp/pr-body.md left over from an earlier session makes the write fail with "Error writing file." Name the file after the branch's task/issue ID so it is both unique per PR and easy to trace: /tmp/pr-body-DEV-1860.md, /tmp/pr-body-issue-11832.md. If that path somehow already exists, append a short unique suffix (e.g. /tmp/pr-body-DEV-1860-2.md).

The correct workflow:

  1. Write the body to /tmp/pr-body-<task-id>.md using the Write tool (no shell escaping needed).
  2. Pass it with --body-file /tmp/pr-body-<task-id>.md.
# Step 1: write body to file first (use the Write tool, not shell echo/cat)
#         e.g. /tmp/pr-body-DEV-1860.md
# Step 2: create the PR
gh pr create --draft --base develop \
  --title "DEV-xxx: Short description" \
  --body-file /tmp/pr-body-DEV-xxx.md

The body file template (write this with the Write tool, backticks and all, no escaping):

### Context

<why this change is needed; link the task and explain the problem>

### How has this been tested?

- <tests you added or ran, with commands>

### Types of changes

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature or improvement (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Additional language file or change to the existing one (translations)

### Related issue(s):

1. DEV-xxx

### Affected project(s):

- [x] `handsontable`
- [ ] `@handsontable/angular-wrapper`
- [ ] `@handsontable/react-wrapper`
- [ ] `@handsontable/vue3`

### Checklist:

- [x] I have reviewed the guidelines about [Contributing to Handsontable](https://github.com/handsontable/handsontable/blob/master/CONTRIBUTING.md) and I confirm that my code follows the code style of this project.
- [x] I have signed the [Contributor License Agreement](https://cla.handsontable.com/sign) — one signature covers both Handsontable and HyperFormula; the `cla/signed` check on this PR confirms it.
- [ ] My change requires a change to the documentation.

ClickUp task: https://app.clickup.com/t/9015210959/DEV-xxx
  • Commit messages: Descriptive, max 80 characters. Include task ID (e.g. DEV-627: Fix filter column index).

  • Include the ClickUp task ID in the PR title when applicable.

  • Start the Context section with "The PR fixes/adds/changes/..." -- be direct, no filler.

  • If the PR introduces a breaking change, require the Breaking change label and include a migration section with before/after examples. Update migration guides in docs/content/guides/upgrade-and-migration/.

  • If you tick "MANUAL QA NEEDED" in the checklist, also apply the red Requires Manual QA label so the request is visible in the PR list. Nothing applies it automatically — labels in this repo are applied by hand:

    gh pr edit <number> --add-label "Requires Manual QA"
    

    The label already exists in this repository — do not create it. gh label list --search "Manual QA" also returns QA needed and Verified by QA, which are different labels with their own meanings, so match the name exactly rather than the closest hit. Creating a near-miss name (Manual QA required) silently makes a second red label that nobody filters on.

    The label is a marker only. The gate is the ticked box, which the Checks scope router reads when the pipeline starts: it holds Manual QA / sign-off until a designated reviewer approves the run. Because the box is read once per run, ticking it after a pipeline has already gone green does not arm anything — press "Re-run all jobs" on the Tests run (and the same applies in reverse after unticking).

5a. Updating an Existing PR's Body

When asked to update, fix, or re-fill a PR description, use the same temp-file approach with a unique, task-scoped filename: write the body to /tmp/pr-body-<task-id>.md (e.g. /tmp/pr-body-DEV-1860.md) with the Write tool, then gh pr edit <number> --body-file /tmp/pr-body-<task-id>.md. A fixed /tmp/pr-body.md fails when a stale copy from an earlier session exists, because the Write tool will not overwrite a file it has not read this session. Keep the full template structure — do not replace it with a shorter summary. Never use --body "$(cat <<'EOF'...EOF)" — backticks are not shell-escaped in the Write tool output and will be stored as literal ``` on GitHub.

6. Changelog Entry (after PR is created)

Every PR that changes source code needs a changelog entry in .changelogs/. bin/changelog names the file after the entry's issueOrPR field, so the filename is the PR number only for a private entry — the default, and what the rest of this section assumes. A public entry is named after its GitHub issue number instead, and because that number is known before the PR exists, it can be committed together with the code rather than in the round-trip below.

Which issuesOrigin to use is decided by .changelogs/README.md, not here — read it before writing the entry.

Two blocking checks constrain the entry, so get both right the first time. The filename must be <issueOrPR>.json, a plain number with no suffix, and it must match the entry's issueOrPR field — bin/changelog fails the changelog job over a mismatch, and the pre-push hook fails locally. And the PR may add at most two entry files, the second only for a separate GitHub issue it closes; a maintenance PR back-filling entries for other PRs writes [multiple changelogs] in the description to lift that. Running npm run changelog entry satisfies the filename rule by construction; writing the JSON by hand is what breaks it.

For a private entry, after writing the file:

  1. Commit it on the same branch (DEV-xxx: Add changelog entry for PR #<number>).
  2. Push so the PR picks up the new commit.

Use [skip changelog] in the PR body only for test-only, docs-only, or CI/tooling changes. When skipping, you do not create a PR-first round-trip — just open the PR and be done.

7. After PR Creation

When working from a ClickUp task, use the ClickUp MCP tools to update the task status to "code review".

8. Merge Strategy

All PRs are merged using "Squash and merge". The squashed commit message becomes the permanent history, so make sure the PR title is clear and descriptive.

Signals

GitHub stars
22k
Forks
3k
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
pr-creation-handsontable
Source
github.com/handsontable/handsontable