3,976
268
# Install this skill:
npx skills add EpicenterHQ/epicenter --skill "git"

Install specific skill from multi-skill repository

# Description

Git commit and pull request guidelines using conventional commits. Use when creating commits, writing commit messages, creating PRs, or reviewing PR descriptions.

# SKILL.md


name: git
description: Git commit and pull request guidelines using conventional commits. Use when creating commits, writing commit messages, creating PRs, or reviewing PR descriptions.


Git Commit and Pull Request Guidelines

Conventional Commits Format

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Commit Types

  • feat: New features (correlates with MINOR in semantic versioning)
  • fix: Bug fixes (correlates with PATCH in semantic versioning)
  • docs: Documentation only changes
  • refactor: Code changes that neither fix bugs nor add features
  • perf: Performance improvements
  • test: Adding or modifying tests
  • chore: Maintenance tasks, dependency updates, etc.
  • style: Code style changes (formatting, missing semicolons, etc.)
  • build: Changes to build system or dependencies
  • ci: Changes to CI configuration files and scripts

Scope Guidelines

  • Scope is OPTIONAL: only add when it provides clarity
  • Use lowercase, placed in parentheses after type: feat(transcription):
  • Prefer specific component/module names over generic terms
  • Your current practice is good: component names (EditRecordingDialog), feature areas (transcription, sound)
  • Avoid overly generic scopes like ui or backend unless truly appropriate

When to Use Scope

  • When the change is localized to a specific component/module
  • When it helps distinguish between similar changes
  • When working in a large codebase with distinct areas

When NOT to Use Scope

  • When the change affects multiple areas equally
  • When the type alone is sufficiently descriptive
  • For small, obvious changes

Description Rules

  • Start with lowercase immediately after the colon and space
  • Use imperative mood ("add" not "added" or "adds")
  • No period at the end
  • Keep under 50-72 characters on first line

Breaking Changes

  • Add ! after type/scope, before colon: feat(api)!: change endpoint structure
  • Include BREAKING CHANGE: in the footer with details
  • These trigger MAJOR version bumps in semantic versioning

Examples Following Your Style:

  • feat(transcription): add model selection for OpenAI providers
  • fix(sound): resolve audio import paths in assets module
  • refactor(EditRecordingDialog): implement working copy pattern
  • docs(README): clarify cost comparison section
  • chore: update dependencies to latest versions
  • fix!: change default transcription API endpoint

Commit Messages Best Practices

The "Why" is More Important Than the "What"

The commit message subject line describes WHAT changed. The commit body explains WHY.

Good commit (explains motivation):

fix(auth): prevent session timeout during file upload

Users were getting logged out mid-upload on large files because the
session refresh only triggered on navigation, not background activity.

Bad commit (only describes what):

fix(auth): add keepalive call to upload handler

The first commit tells future developers WHY the code exists. The second makes them dig through the code to understand the purpose.

Other Best Practices

  • NEVER include Claude Code or opencode watermarks or attribution
  • Each commit should represent a single, atomic change
  • Write commits for future developers (including yourself)
  • If you need more than one line to describe what you did, consider splitting the commit

Pull Request Guidelines

Motivation First

Every PR description MUST start with WHY this change exists. Not what files changed, not how it worksβ€”WHY.

Good PR opening:

Users were getting logged out mid-upload on large files. The session refresh only triggered on navigation, not during background activity like uploads.

Bad PR opening:

This PR adds a keepalive call to the upload handler and updates the session refresh logic.

The reader should understand the PROBLEM before they see the SOLUTION.

Code Examples Are Mandatory for API Changes

If the PR introduces or modifies APIs, you MUST include code examples showing how to use them. No exceptions.

What requires code examples:

  • New functions, types, or exports
  • Changes to function signatures
  • New CLI commands or flags
  • New HTTP endpoints
  • Configuration changes

Good API PR (shows the actual usage):

// Define actions once
const actions = {
    posts: {
        create: defineMutation({
            input: type({ title: 'string' }),
            handler: ({ title }) => client.tables.posts.create({ title }),
        }),
    },
};

// Pass to adapters - they generate CLI commands and HTTP routes
const cli = createCLI(client, { actions });
const server = createServer(client, { actions });

Bad API PR (only describes without showing):

This PR adds an action system that generates CLI commands and HTTP routes from action definitions.

The first version lets reviewers understand the API at a glance. The second forces them to dig through the code to understand the call sites.

Visual Communication with ASCII Art

Use ASCII diagrams liberally to communicate complex ideas. They're more scannable than prose and show relationships at a glance.

Journey/Evolution Diagrams

For PRs that iterate on previous work, show the evolution:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PR #1217 (Jan 7)                                                       β”‚
β”‚  "Add YKeyValue for 1935x storage improvement"                          β”‚
β”‚                                                                         β”‚
β”‚       Y.Map (524,985 bytes) ──→ YKeyValue (271 bytes)                   β”‚
β”‚                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                                    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PR #1226 (Jan 8)                                                       β”‚
β”‚  "Remove YKeyValue, use native Y.Map + epoch compaction"                β”‚
β”‚                                                                         β”‚
β”‚  Reasoning: "Unpredictable LWW behavior"  ← ⚠️ (misleading!)            β”‚
β”‚                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                                    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  This PR                                                                β”‚
β”‚  "Restore YKeyValue with LWW timestamps"                                β”‚
β”‚                                                                         β”‚
β”‚  Why: Timestamp-based resolution gives intuitive "latest wins"          β”‚
β”‚                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Layered Architecture Diagrams

Show how components stack:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  defineWorkspace() + workspace.create()                     β”‚  ← High-level
β”‚    Creates Y.Doc internally, binds tables/kv/capabilities   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  createTables(ydoc, {...}) / createKv(ydoc, {...})          β”‚  ← Mid-level
β”‚    Binds to existing Y.Doc                                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  defineTable() / defineKv()                                 β”‚  ← Low-level
β”‚    Pure schema definitions                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Comparison Tables

For showing trade-offs between approaches:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Use Case                         β”‚  Recommendation            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Real-time collab, simple cases   β”‚  YKeyValue (positional)    β”‚
β”‚  Offline-first, multi-device      β”‚  YKeyValueLww (timestamp)  β”‚
β”‚  Clock sync unreliable            β”‚  YKeyValue (no clock dep)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Flow Diagrams

For showing data/control flow:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Conflict Resolution                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                β”‚
β”‚  Client A (2:00pm)  ──┐                                        β”‚
β”‚                       │──→  Sync  ──→  Winner?                 β”‚
β”‚  Client B (3:00pm)  β”€β”€β”˜                                        β”‚
β”‚                                    β”‚                           β”‚
β”‚                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚                   β–Ό                                 β–Ό          β”‚
β”‚             YKeyValue                         YKeyValueLww     β”‚
β”‚          (clientID wins)                   (timestamp wins)    β”‚
β”‚           ~50% correct                       100% correct      β”‚
β”‚                                                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

When to Use Diagrams

  • Journey diagrams: PR iterates on previous work or fixes a past decision
  • Layer diagrams: PR introduces or changes architecture with distinct levels
  • Comparison tables: PR introduces alternatives or explains trade-offs
  • Flow diagrams: PR changes how data or control moves between components

ASCII art characters to use: β”Œ ┐ β”” β”˜ ─ β”‚ β”œ ─ ┬ β”΄ β”Ό β–Ό β–² β—€ β–Ά ──→ ←── ⚠️ βœ… ❌

Other Guidelines

  • NEVER include Claude Code or opencode watermarks or attribution in PR titles/descriptions
  • PR title should follow same conventional commit format as commits
  • Focus on the "why" and "what" of changes, not the "how it was created"
  • Include any breaking changes prominently
  • Link to relevant issues

Verifying GitHub Usernames

CRITICAL: When mentioning GitHub users with @username in PR descriptions, issue comments, or any GitHub content, NEVER guess or assume usernames. Always verify programmatically using the GitHub CLI:

# Get the author of a PR
gh pr view <PR_NUMBER> --json author

# Get the author of an issue
gh issue view <ISSUE_NUMBER> --json author

This prevents embarrassing mistakes where you credit the wrong person. Always run the verification command before writing the @mention.

Merge Strategy

When merging PRs, use regular merge commits (NOT squash):

gh pr merge --merge  # Correct: preserves commit history
# NOT: gh pr merge --squash
# NOT: gh pr merge --rebase

# Use --admin flag if needed to bypass branch protections
gh pr merge --merge --admin

Preserve individual commits; they tell the story of how the work evolved.

Pull Request Body Format

Use clean paragraph format instead of bullet points or structured sections:

First Paragraph: Explain what the change does and what problem it solves.

  • Focus on the user-facing benefit or technical improvement
  • Use clear, descriptive language about the behavior change

Subsequent Paragraphs: Explain how the implementation works.

  • Describe the technical approach taken
  • Explain key classes, methods, or patterns used
  • Include reasoning for technical decisions (e.g., why flex-1 is needed)

Example:

This change enables proper vertical scrolling for drawer components when content exceeds the available drawer height. Previously, drawers with long content could overflow without proper scrolling behavior, making it difficult for users to access all content and resulting in poor mobile UX.

To accomplish this, I wrapped the `{@render children?.()}` in a `<div class="flex-1 overflow-y-auto">` container. The `flex-1` class ensures the content area takes up all remaining space after the fixed drag handle at the top, while `overflow-y-auto` enables vertical scrolling when the content height exceeds the available space. This maintains the drag handle as a fixed element while allowing the content to scroll independently, preserving the expected drawer interaction pattern.

Body Structure

  1. Context Section (if needed for complex changes):
  2. Use bullet points for multiple related observations
  3. Mix technical detail with accessible explanations
  4. Acknowledge trade-offs: "we'd like to X, but at the same time Y is problematic"

  5. Solution Description:

  6. Lead with what changed in plain language
  7. Show code examples inline to illustrate the improvement
  8. Compare before/after when it clarifies the change

  9. Technical Details:

  10. Explain the "why" behind architectural decisions
  11. Reference philosophical goals: "This doubles down on what people love about..."
  12. Connect to long-term vision when relevant

  13. Outstanding Work (if applicable):

  14. List TODOs candidly
  15. Be specific about what remains
  16. No need to apologize; just state what's left

Voice and Tone

  • Conversational but precise: Write like explaining to a colleague
  • Direct and honest: "This has been painful" rather than "This presented challenges"
  • Show your thinking: "We considered X, but Y made more sense because..."
  • Use "we" for team decisions, "I" for personal observations

Example PR Description:

This fixes the long-standing issue with nested reactivity in state management.

First, some context: users have consistently found it cumbersome to create deeply reactive state. The current approach requires manual get/set properties, which doesn't feel sufficiently Svelte-like. Meanwhile, we want to move away from object mutation for future performance optimizations, but `obj = { ...obj, x: obj.x + 1 }` is ugly and creates overhead.

This PR introduces proxy-based reactivity that lets you write idiomatic JavaScript:

```javascript
let todos = $state([]);
todos.push({ done: false, text: 'Learn Svelte' }); // just works
```

Under the hood, we're using Proxies to lazily create signals as necessary. This gives us the ergonomics of mutation with the performance benefits of immutability.

Still TODO:
- Performance optimizations for large arrays
- Documentation updates
- Migration guide for existing codebases

This doubles down on Svelte's philosophy of writing less, more intuitive code while setting us up for the fine-grained reactivity improvements planned for v6.

What to Avoid

  • Listing files changed: Never enumerate which files were modified. GitHub's "Files changed" tab already shows this; the PR description should explain WHY, not WHAT files
  • Bullet points or structured lists
  • Section headers like "## Summary" or "## Changes Made"
  • Test plans or checklists (unless specifically requested)
  • Marketing language or excessive formatting
  • Corporate language: "This PR enhances our solution by leveraging..."
  • Excessive structure: Multiple heading levels and subsections
  • Marketing speak: "game-changing", "revolutionary", "seamless"
  • Over-explaining simple changes
  • Apologetic tone for reasonable decisions

What NOT to Include:

  • Generated with [Claude Code](https://claude.ai/code)
  • Co-Authored-By: Claude <[email protected]>
  • Any references to AI assistance
  • Generated with [opencode](https://opencode.ai)
  • Co-Authored-By: opencode <[email protected]>
  • Tool attribution or watermarks

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.