Contribution TODO¶
Concrete, well-scoped tasks for new contributors. Each task is completable in 1--5 hours. Frontend tasks require basic React/TypeScript. Backend tasks require zero prior Rust experience.
Two junior developers are joining. They have no Rust experience and some frontend experience.
Frontend¶
1. Add loading skeletons to message detail panel¶
Difficulty: Easy
MessageDetail.tsx shows plain "Loading..." text while fetching. Replace with skeleton loaders using Tailwind animate-pulse that match the layout structure (subject, from/date, tags, body frame).
Files: apps/web/src/components/MessageDetail.tsx
Depends on: nothing
Skills learned: React Query loading states, Tailwind animations, component composition
2. Add aria-labels and keyboard shortcut hints to toolbar buttons¶
Difficulty: Easy
Toolbar buttons (Archive, Trash, Flag) in App.tsx have title attributes but lack aria-label for screen readers. Update titles to show keyboard shortcuts (e.g. "Archive (e)", "Trash (#)").
Files: apps/web/src/App.tsx
Depends on: nothing
Skills learned: Web accessibility (ARIA), semantic HTML
3. Create error boundary component¶
Difficulty: Medium
No top-level error boundary exists. Create ErrorBoundary.tsx that catches React rendering errors, shows a user-friendly UI with error details in dev mode only, and provides a "Reload" button.
Files: new apps/web/src/components/ErrorBoundary.tsx, apps/web/src/App.tsx
Depends on: nothing
Skills learned: React error boundaries, error recovery UX
4. Add empty state illustrations¶
Difficulty: Easy
Several components show plain text for empty states ("No threads in this view", "Select a message"). Replace with lucide-react icons + descriptive message + next-step hints.
Files: apps/web/src/components/MessageList.tsx, apps/web/src/components/MessageDetail.tsx, apps/web/src/components/Sidebar.tsx
Depends on: nothing
Skills learned: UI/UX polish, icon usage
5. Add copy-to-clipboard for email addresses¶
Difficulty: Easy
In MessageDetail, the sender email is displayed but not copyable. Add a small copy icon button with tooltip and brief "Copied!" feedback using navigator.clipboard.writeText().
Files: apps/web/src/components/MessageDetail.tsx
Depends on: nothing
Skills learned: Clipboard API, micro-interactions
6. Add unread count to browser tab title¶
Difficulty: Easy
Browser tab just says "PostHaste". Create a useDocumentTitle() hook that computes total unread from normalized mail-navigation data and updates document.title (e.g. "PostHaste (3)").
Files: new apps/web/src/hooks/useDocumentTitle.ts, apps/web/src/App.tsx
Depends on: nothing
Skills learned: Document API, custom hooks, side effects
7. Improve message row typography hierarchy¶
Difficulty: Easy
MessageRow uses font-semibold for unread but overall hierarchy (sender, subject, preview) could be sharper. Review font weights, padding, and gap for better readability.
Files: apps/web/src/components/MessageRow.tsx
Depends on: nothing
Skills learned: Typography, Tailwind, visual hierarchy
8. Add confirmation dialog for destructive actions¶
Difficulty: Medium
Trashing messages via keyboard shortcut (#) has no confirmation. Create a reusable ConfirmDialog.tsx with Escape-to-cancel and message preview.
Files: new apps/web/src/components/ConfirmDialog.tsx, apps/web/src/hooks/useEmailActions.ts, apps/web/src/components/MessageList.tsx
Depends on: nothing
Skills learned: Dialog patterns, keyboard handling, state management
9. Polish search bar clear button and focus management¶
Difficulty: Easy
Search bar in App.tsx expands on focus but the clear button is subtle. Improve hover state, add "Escape to clear" hint, and consider input debounce.
Files: apps/web/src/App.tsx
Depends on: nothing
Skills learned: UX polish, keyboard shortcuts, input handling
10. Create formatBytes() utility with tests¶
Difficulty: Easy
Messages have size info but it is never displayed. Create apps/web/src/utils/formatBytes.ts with proper unit formatting (B, KB, MB, GB) and write Vitest tests for it.
Files: new apps/web/src/utils/formatBytes.ts, new apps/web/src/utils/__tests__/formatBytes.test.ts
Depends on: Task 25 (testing framework)
Skills learned: Utility functions, testing patterns
11. Add focus-visible styles to all interactive elements¶
Difficulty: Medium
Keyboard navigation needs visible focus indicators. Add focus-visible:ring-1 focus-visible:ring-ring to all buttons and clickable elements. Test tab order (sidebar -> list -> detail).
Files: apps/web/src/App.tsx, apps/web/src/components/Sidebar.tsx, apps/web/src/components/MessageList.tsx, others
Depends on: nothing
Skills learned: Accessibility, Tailwind focus states, keyboard navigation
12. Add loading skeleton to settings panel¶
Difficulty: Medium
SettingsPanel loads settings and smart mailboxes but has no skeleton. Add progressive loading UI to avoid flash of empty state.
Files: apps/web/src/components/SettingsPanel.tsx
Depends on: Task 1 (pattern established)
Skills learned: React Query states, progressive loading
13. Add tooltip to unread indicator dot¶
Difficulty: Easy
The small blue dot in MessageRow for unread status is not immediately obvious. Add a hover tooltip "Unread message".
Files: apps/web/src/components/MessageRow.tsx
Depends on: nothing
Skills learned: Tooltips, hover states
14. Extract message list virtualization constants to config¶
Difficulty: Easy
MessageList.tsx has magic numbers (PAGE_SIZE, ROW_HEIGHT, OVERSCAN_ROWS). Extract to apps/web/src/config/messageListConfig.ts with comments explaining each.
Files: new apps/web/src/config/messageListConfig.ts, apps/web/src/components/MessageList.tsx
Depends on: nothing
Skills learned: Code organization, configuration management
Backend¶
15. Extract error code constants¶
Difficulty: Easy
API error codes ("NOT_FOUND", "INVALID_REQUEST") are scattered as string literals in account_support.rs and cursor_support.rs. Create error_codes.rs with named constants and update handlers.
Files: new crates/posthaste-server/src/error_codes.rs, crates/posthaste-server/src/api/account_support.rs, crates/posthaste-server/src/api/cursor_support.rs
Depends on: nothing
Skills learned: Rust modules, constants, DRY principle
16. Add request/response logging middleware¶
Difficulty: Medium
The API has no structured logging. Add tracing middleware that logs HTTP method, path, status code, and duration for every request.
Files: new crates/posthaste-server/src/logging.rs, crates/posthaste-server/src/main.rs
Depends on: nothing
Skills learned: Rust logging (tracing), middleware, observability
17. Add health check endpoint¶
Difficulty: Easy
Add GET /v1/health that returns {"status":"ok"} (200) or 503 if the database is unavailable. Useful for monitoring and deployment.
Files: crates/posthaste-server/src/api.rs, crates/posthaste-server/src/main.rs
Depends on: nothing
Skills learned: Axum routing, async functions, HTTP status codes
18. Add rate limiting to API routes¶
Difficulty: Medium
The API has no rate limiting. Add tower-governor or similar middleware with 429 responses when limits are exceeded.
Files: crates/posthaste-server/src/main.rs
Depends on: nothing
Skills learned: Rust middleware, rate limiting, security
19. Document API error responses¶
Difficulty: Easy
Create a reference document listing all error codes with HTTP status, example JSON, cause, and fix guidance.
Files: new docs/api-errors.md
Depends on: Task 15 (error constants)
Skills learned: Technical documentation, API design
20. Add validation tests for account settings¶
Difficulty: Medium
Account validation logic in account_support.rs has no unit tests. Write tests for valid configs, invalid URLs, missing fields, and edge cases.
Files: crates/posthaste-server/src/api/account_support.rs (add #[cfg(test)] module)
Depends on: nothing
Skills learned: Rust unit testing, validation logic
21. Document posthaste-config schema¶
Difficulty: Easy
Config uses TOML but the schema is undocumented. Create docs/config-schema.md with field types, defaults, constraints, and a complete example.
Files: new docs/config-schema.md
Depends on: nothing
Skills learned: Configuration documentation, TOML
22. Document database schema¶
Difficulty: Easy
SQLite schema is defined in code but not documented. Create docs/database-schema.md with tables, columns, keys, indexes, and a text-based ER diagram.
Files: new docs/database-schema.md
Depends on: nothing
Skills learned: Database documentation, data modeling
23. Extract SQL constants from posthaste-store¶
Difficulty: Easy
Various Rust files have hardcoded table/column name strings. Create crates/posthaste-store/src/db/constants.rs and update imports.
Files: new crates/posthaste-store/src/db/constants.rs, crates/posthaste-store/src/db.rs
Depends on: nothing
Skills learned: Rust modules, constants, maintainability
24. Add doc comments to public functions¶
Difficulty: Easy
Public functions in posthaste-server and posthaste-store lack /// doc comments. Add them and verify with cargo doc --open.
Files: multiple files in crates/posthaste-server/src/api/
Depends on: nothing
Skills learned: Rust doc comments, cargo doc
Project Infrastructure¶
25. Set up frontend testing framework (Vitest + RTL)¶
Difficulty: Medium
Zero frontend tests exist. Add vitest, @testing-library/react, and @testing-library/user-event. Create vitest.config.ts, a test script, and one example test for formatRelativeTime.
Files: apps/web/package.json, new apps/web/vitest.config.ts, new apps/web/src/utils/formatRelativeTime.test.ts
Depends on: nothing
Skills learned: Test framework setup, Bun tooling
26. Add pre-commit hook for linting¶
Difficulty: Medium
Add a hook that runs bun run lint in apps/web/ and cargo clippy --workspace from the repository root before each commit.
Files: hook script, README.md update
Depends on: nothing
Skills learned: Git hooks, shell scripting, CI/CD
27. Document development setup in README¶
Difficulty: Easy
README lacks step-by-step setup instructions. Add prerequisites, clone-and-run steps, how to start both dev servers, env var examples, and troubleshooting.
Files: README.md
Depends on: nothing
Skills learned: Technical writing, developer onboarding
28. Create CONTRIBUTING.md¶
Difficulty: Easy
Add contribution guidelines: branch/commit conventions, where to find tasks (this file), code style, how to run tests, PR process.
Files: new CONTRIBUTING.md
Depends on: nothing
Skills learned: Open source practices, documentation
29. Add CI workflow (GitHub Actions)¶
Difficulty: Medium
No CI pipeline. Create .github/workflows/test.yml that runs frontend lint+test and backend cargo test+cargo clippy on every push and PR. Add status badge to README.
Files: new .github/workflows/test.yml, README.md
Depends on: Task 25 (frontend tests)
Skills learned: GitHub Actions, CI/CD pipelines
30. Create issue templates¶
Difficulty: Easy
Add .github/ISSUE_TEMPLATE/bug_report.md and feature_request.md with structured sections (description, repro steps, environment, screenshots).
Files: new .github/ISSUE_TEMPLATE/bug_report.md, new .github/ISSUE_TEMPLATE/feature_request.md
Depends on: nothing
Skills learned: Issue management, community guidelines
Onboarding Plan¶
Week 1 (high-impact quick wins)¶
| Task | Why first |
|---|---|
| 27 — Dev setup docs | Forces them to set up the project and document gaps |
| 25 — Testing framework | Unblocks all future frontend tests |
| 1 — Loading skeletons | Teaches React Query + Tailwind patterns |
| 2 — Accessibility labels | Quick win, immediate UX improvement |
Week 2 (expanding skills)¶
| Task | Why next |
|---|---|
| 3 — Error boundary | Advanced React pattern |
| 10 — formatBytes + tests | First test-writing exercise |
| 15 — Error code constants | Intro to Rust, minimal domain knowledge |
| 24 — Doc comments | Reading and understanding Rust code |
Week 3+ (polish and infrastructure)¶
Pick from remaining tasks based on interest. Frontend-leaning devs: 4--14. Backend-curious devs: 16--23. Infra-minded: 26, 28--30.
Milestones¶
End of Week 1: Can run both dev servers, made 1--2 merged contributions, understands component architecture.
End of Week 2: Comfortable with testing patterns, familiar with Rust module structure, understands API boundary.
End of Month 1: Can implement a full React component with error handling, can write basic Rust following project patterns, understands frontend-backend communication.