Skip to content

Latest commit

 

History

History
370 lines (256 loc) · 14 KB

File metadata and controls

370 lines (256 loc) · 14 KB

Contributing Guide

Thanks for considering a contribution. This guide covers the development environment, code conventions, and the submission process — everything you need to open a PR.


Table of Contents


Getting Started

Prerequisites

Engines can be installed manually or via Settings → System Tools after first launch.

Clopen is a Bun-only project — Node.js and Deno are not supported. Use bun for all package management and scripts.

Setup

Fork the repository via the GitHub UI, then:

git clone https://github.com/YOUR_USERNAME/clopen.git
cd clopen
git remote add upstream https://github.com/myrialabs/clopen.git
bun install
bun run check

bun run check should pass cleanly on a fresh clone. If it doesn't, see Troubleshooting.

Keep Your Fork Updated

Before starting any new branch, sync with upstream:

git fetch upstream
git checkout main
git merge upstream/main
git push origin main   # optional: also sync your fork's main on GitHub

Development Data Directory

When running bun run dev, Clopen stores data in ~/.clopen-dev instead of ~/.clopen. This keeps development data separate from any production instance — especially important since Clopen can be used to develop itself.


Development Workflow

Standard flow from branch creation to PR:

# 1. Sync
git checkout main && git pull upstream main

# 2. Branch
git checkout -b feature/your-feature

# 3. Develop & verify locally
bun run check && bun run lint && bun run build

# 4. Commit
git commit -m "feat(scope): description"

# 5. Sync with upstream main again (resolve conflicts locally, not in the GitHub UI)
git fetch upstream && git merge upstream/main
bun run check && bun run lint && bun run build   # re-verify after merge

# 6. Push & open PR
git push origin feature/your-feature

Open the PR targeting main on GitHub. See Submitting Changes for the conventions used at each step.


Code Style

TypeScript

  • Use const by default; use let only when reassignment is needed.
  • any is acceptable for Elysia/WS patterns where strict typing creates friction.

Svelte 5

Use the runes system, not legacy stores. let for $state and $bindable; const for everything else ($derived, $props, functions).

<script lang="ts">
  let count = $state(0);
  const doubled = $derived(count * 2);
  const handleClick = () => count++;
</script>

Naming

  • camelCase — variables, functions
  • PascalCase — classes, types
  • UPPER_SNAKE_CASE — constants
  • kebab-case — file names

Logging

Use the project's debug module instead of console.*. It respects log levels and namespaces.

import { debug } from './utils/debug';

debug.log('namespace', 'message', { data });

Formatting

Tabs, single quotes, semicolons. No Prettier enforcement — manual consistency.

Tests

Add a *.test.ts file when the change introduces non-trivial logic where a regression would be silent or costly — validators, parsers, security checks, path resolution, anything with branching that isn't obvious by inspection. Place the test alongside the source: foo.tsfoo.test.ts. Use bun:test.

Skip tests for changes that are inherently observable (UI tweaks, log strings, dependency bumps), trivial one-liners, or thin forwards of an already-tested function. If breaking the code would take more than a glance to notice, add a test.

For security fixes, tests are expected — at minimum a case that fails before the patch and passes after. Cover the boundary explicitly (the exact value that should be rejected) and at least one happy path.

bun test path/to/file.test.ts   # single file
bun test                        # full suite

Tests must pass before opening the PR.


Submitting Changes

All written content on the repository — branch names, commit messages, PR titles, PR descriptions, and PR comments — must be in English, regardless of the language you and the maintainers use elsewhere. This keeps the contribution trail readable across the project's audience.

Branch Naming

Format: <type>/<description> — lowercase, kebab-case, concise. Exactly one /: the type, then the description. Nested paths are not allowed.

Type Use
feature/ New feature
fix/ Bug fix
docs/ Documentation
chore/ Build, refactor, dependencies, miscellaneous

Examples: feature/database-management, fix/websocket-connection, docs/maintainers-guide-restructure.

Don't:

Wrong Why Fix
docs/maintainers/review-examples Two slashes — nested path docs/maintainers-review-examples
Fix/Websocket-Connection Uppercase fix/websocket-connection
fix/fix-the-websocket-connection-bug-that-happens-on-reconnect Verbose fix/websocket-reconnect
feature/auth.middleware Non-kebab punctuation feature/auth-middleware

Commit Messages

Format: <type>(<scope>): <subject> — imperative mood, lowercase, no period, max 72 characters.

Type Use
feat New feature
fix Bug fix
docs Documentation
chore Refactor, build, perf, dependencies
release Version release

Examples:

feat(chat): add message export
fix(terminal): resolve memory leak
docs(maintainers): document pr reshape workflow
chore: update dependencies

Keep the subject focused on why the change exists, not what files moved. If the change needs a longer explanation, put it in the PR description, not the commit body — repo convention is subject-only.

Pre-commit Checklist

Before pushing each commit:

  • bun run check passes
  • bun run lint passes
  • bun run build works
  • bun test passes if any test file was added or modified
  • New non-trivial logic has a *.test.ts (see Tests)
  • Commit message follows the format above
  • No console.* (use the debug module)
  • No sensitive data (tokens, credentials, internal URLs)

If a pre-commit hook fails, fix the underlying issue rather than skipping with --no-verify. Hooks exist for the same reasons CI does.

Pull Request Format

Title

Same format as commit messages: <type>(<scope>): <subject> (lowercase, imperative, no period, ≤72 chars). GitHub uses this as the squash-commit subject on main.

Description Template

## Summary
One or two sentences: what this PR does.

## Why
The motivation — bug it fixes, behavior it changes, constraint it addresses.

## Changes
- bullet list of concrete changes (files, modules, behaviors)

## Notes (optional)
Anything reviewers should know: trade-offs, follow-ups, areas needing extra eyes.

Optional Sections

Add only when relevant — empty headers add noise:

Section When to use
## Test plan Any non-trivial change. Bulleted checklist of how the change was verified (bun run check, manual UI test, regression test, etc.).
## Security impact Any change touching auth, authorization, input validation, file/path handling, or external execution. State the threat model in one paragraph: who is the attacker, what they could reach before, what is closed now.
## Breaking changes Public API, WebSocket schema, DB schema, or config keys changed. Include migration path.
## Migration Steps existing installs need to follow (DB migration, config update, manual cleanup).
## Screenshots UI changes — before/after images or a short clip.
## Follow-ups Known TODOs deferred to a separate PR (with link/issue if one exists).
## Related Links to related issues, PRs, or external discussions.

Comments on Existing PRs

Match the comment's shape to its substance. Most contributor comments are short replies and read best as prose; a few situations warrant lists or topic sections so reviewers can scan.

The examples below are illustrative templates, not literal copy-paste — angle-bracket slots like <the specific reasoning> and concrete-looking names should be replaced with what's actually in your PR. Structural pieces (the opening "Thanks for…", the closing commitment, ## Header shapes) are the lesson; keep those.

Prose — the default. Short replies, single-topic responses, agreements, clarifications. Open with one or two sentences that name something specific you took from the review (the catch, the file:line reference, the reasoning) — generic "thanks" is filler. Then raise your reply, concern, or counter in prose, anchored to file:line inline. Warmth and brevity are not opposites.

Thanks for the careful breakdown. I re-validated the flow and I agree with your assessment: `<the specific point the maintainer made, in your own words>`. So `<the practical consequence — why the original fix shape doesn't apply, or what changes in your understanding>`.

I'm closing this PR to avoid carrying the wrong abstraction. If `<the condition that would make the original direction relevant>` changes in the future, I'll open a separate PR with `<the alternative approach — the layer it belongs at, the test that would pin it>`.

Validation-result list. When responding to a maintainer's "please verify end-to-end" request, a bulleted list of what you ran is appropriate — it's concrete evidence, not framing. Keep the items factual; one line each. Only include items you actually ran; if the maintainer didn't ask for a manual UI walkthrough, the universal commands alone are fine.

Thanks for the guidance. I re-tested this PR locally after installing Bun.

Validation performed:
- `bun run check` passes with 0 errors / 0 warnings.
- `bun run lint` passes.
- Started the app locally with `bun run dev` and exercised <the specific flow the maintainer asked about>.

<Optional one-sentence summary tying the validation back to the maintainer's concern, if it isn't obvious from the items above.>

If the maintainer asked you to verify multiple distinct scenarios (e.g. a positive case and a negative case for an authorization change), add one bullet per scenario — each describing what you did and what you observed, not just "verified X". The bullets mirror what was actually asked; they aren't a fixed checklist.

Topic sections (## <Topic Name>) — use when:

  • You pushed commits or expanded scope and the additions span multiple files/areas. Mirror the PR-description shape (## Summary / ## Why / ## Changes / ## Notes) so reviewers can scan what's new.
  • Your reply addresses multiple distinct concerns the maintainer raised (e.g. a blocker + a process question + a follow-up commitment). One ## per class, prose inside.

For everything else — single reply, agreement with the audit, clarification of one detail — prose is shorter and clearer.

Either way:

  • File paths and identifiers in backticks.
  • @username for mentions.
  • Dates as plain English (May 25, 2026), not ISO format.
  • One issue per paragraph. No restated context.
  • Avoid numbered audit-verdict lists with bolded leads (**1. Issue:** ...) — they read as a checklist handed back, not a conversation.

After You Submit

A maintainer will read the full diff and audit it before responding. You can expect one of these responses:

Response What it means
Approve and merge Audit was clean.
Commits added to your branch Maintainer extended your fix with adjacent changes and posted a summary. Pull before pushing anything new.
Comment requesting discussion Maintainer has concerns or wants to split scope. Default response window is 1 week — silence past the deadline closes the PR as auto-stale, and you can reopen at any time.
Close with reshape Approach needs to change. You'll be credited in the replacement PR — closure is administrative, not rejection.

If you disagree with feedback, push back with a concrete scenario, file/line reference, or counterargument — reviewers expect this and will update their position when warranted.


Reference

Commands

bun run dev          # Dev server
bun run check        # Type check
bun run lint         # Lint
bun run lint:fix     # Auto-fix lint
bun run build        # Build

Troubleshooting

# Type errors
rm -rf node_modules && bun install

# Lint errors
bun run lint:fix

# Git conflicts
git fetch upstream && git rebase upstream/main

Resources


Questions?