Thank you for your interest in contributing to JobSentinel! This guide will help you get started.
If you're an AI assistant (Claude, GPT, Copilot, etc.) working on this codebase:
- Read AGENTS.md.
- Read Harness Engineering.
- Use Change Contract for non-trivial work.
- Run sensors from Verification Matrix.
- Update docs when behavior, setup, architecture, commands, or security changes.
After any significant change, update all relevant docs:
| Change Type | Must Update |
|---|---|
| New feature | CHANGELOG.md, docs/features/, README.md, active plan or roadmap |
| New Tauri command | AGENTS.md if workflow changes, docs/harness/, docs/README.md |
| Bug fix | CHANGELOG.md |
| Refactoring | docs/plans/tech-debt-tracker.md when debt changes |
| New scraper | docs/features/job-sources.md, CHANGELOG.md |
| Test changes | docs/developer/TESTING.md |
Before committing, ask: "Did I update all relevant docs?"
Use the current maintainable file-size policy in
Harness Engineering. The enforceable caps live in
scripts/harness/contracts/repository-structure.json, and npm run lint:bloat fails when a
tracked file exceeds its scope cap or an exception grows past its frozen limit.
Test organization: Move test modules to separate tests.rs files when
tests start to obscure main logic or a file approaches the tracked
maintainability thresholds.
See docs/plans/tech-debt-tracker.md for current refactor and debt items.
Check package.json for the current release package version. See
ROADMAP.md for public product priorities, docs/ROADMAP.md for developer
planning, and docs/plans/tech-debt-tracker.md for current technical debt.
- Code of Conduct
- Getting Started
- Development Workflow
- Coding Standards
- Testing
- Submitting Changes
- Adding New Features
- Reporting Problems
- Improvement Ideas
Be respectful, inclusive, and collaborative. We're all here to make job searching easier.
- Do: Provide constructive feedback
- Do: Help others learn and grow
- Do: Assume good intentions
- Do not: Harass, discriminate, or belittle others
- Do not: Spam or self-promote
- Do not: Share private information
JobSentinel is free, will always stay free, and will always remain MIT licensed. The code is here to help people find work without giving up privacy or control.
Contributions to JobSentinel are welcome. So are forks, adaptations, and better tools built from this code. If this repository helps someone serve more job seekers, that is a good outcome.
Required:
Platform-Specific:
- Windows: Visual Studio Build Tools 2022, Windows 10 SDK
- macOS: Xcode Command Line Tools
- Linux: GTK development libraries
# 1. Fork the repository on GitHub
# 2. Clone your fork
git clone https://github.com/YOUR_USERNAME/JobSentinel.git
cd JobSentinel
# 3. Add upstream remote
git remote add upstream https://github.com/cboyd0319/JobSentinel.git
# 4. Create a branch for your changes
git checkout -b feature/your-feature-name# Synchronize locked dependencies and prove the baseline
./init.sh
# Check Rust compilation
cargo check --workspace# Start the app with hot reload
npm run tauri:dev# Fetch latest changes
git fetch upstream
# Merge into your branch
git checkout main
git merge upstream/main
# Rebase your feature branch
git checkout feature/your-feature-name
git rebase mainFollow the project structure:
src/- React frontend (TypeScript + TailwindCSS)crates/jobsentinel-*/src/- Platform-agnostic code by bounded ownercrates/jobsentinel-platform/src/- OS-specific codesrc-tauri/src/ipc/- Tauri RPC handlers
Use modules before crates. Keep implementation leaves private and expose one owner facade. Add a workspace member only for a distinct runtime, dependency policy, release unit, or stable cross-crate contract.
Keep changes focused:
- One feature/fix per pull request
- Small, atomic commits with clear messages
- Update tests and documentation
# Run Rust tests
cargo test --workspace
# Run with logging
RUST_LOG=debug npm run tauri:dev
# Test on target platforms
# - Windows 11+
# - macOS 26+# Add files
git add .
# Commit with descriptive message
git commit -m "feat(sources): add Acme board adapter
- Register the private adapter through the scraper facade
- Add bounded parser fixtures and rate-limit coverage
- Document the source privacy and access boundary
Closes #123"Commit Message Format:
<type>: <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code formatting (no logic changes)refactor: Code restructuringtest: Adding/updating testschore: Maintenance tasks
All code is DRY and lean. Before writing code, walk the ladder in Engineering Principles and stop at the first step that satisfies the requirement: skip it (YAGNI), use the standard library, use a native platform feature, reuse an installed dependency, or make it one clear line. Boring over clever, and reduce or remove duplication everywhere.
Follow Rust conventions:
# Format code
cargo fmt --all -- --check
# Lint production Rust targets
cargo clippy --workspace -- -D warnings
# Check for security vulnerabilities
cargo deny check advisories bans licenses sourcesBest Practices:
- Use descriptive variable names
- Add doc comments for public APIs
- Handle errors explicitly with domain-specific types when callers need to distinguish failure modes, and preserve dependency errors otherwise
- Use structured error handling with
thiserror(avoid.unwrap()in production) - Treat test-target clippy warnings as advisory unless a change explicitly tightens the test lint policy; production clippy is the hard lint gate.
- Use
tracing::for logging (notprintln!) - Keep functions small and focused
- Sanitize URLs in error messages to prevent information leakage
Example:
/// Scrape jobs from Greenhouse API
///
/// # Arguments
/// * `company` - Company to scrape
///
/// # Returns
/// * `Ok(Vec<Job>)` - List of jobs found
/// * `Err(ScraperError)` - If scraping fails with structured error context
///
/// # Errors
/// Returns `ScraperError` for HTTP errors, parsing failures, or rate limiting
pub async fn scrape_company(&self, company: &GreenhouseCompany) -> ScraperResult {
tracing::info!("Scraping Greenhouse: {}", company.name);
let response = self.client
.get(&company.url)
.send()
.await
.map_err(|e| ScraperError::http_request(&company.url, e))?;
if !response.status().is_success() {
return Err(ScraperError::http_status(
response.status().as_u16(),
&company.url,
"Failed to fetch jobs"
));
}
// Parse and return jobs...
}Follow React best practices:
# Lint and auto-fix code
npm run lint:fix
# Lint code (check only)
npm run lintBest Practices:
- Use functional components with hooks
- Add TypeScript types for all props
- Use TailwindCSS utility classes
- Keep components small and reusable
- Handle loading and error states
Example:
interface JobListProps {
jobs: Job[];
onSelect: (job: Job) => void;
}
export function JobList({ jobs, onSelect }: JobListProps) {
return (
<div className="space-y-4">
{jobs.map((job) => (
<JobCard key={job.id} job={job} onClick={() => onSelect(job)} />
))}
</div>
);
}Write tests for all new code. When tests make main logic harder to read or
push a file toward the tracked maintainability thresholds, move tests to a
separate tests.rs file:
// In src/feature/mod.rs (main logic)
pub fn calculate_score(job: &Job) -> u32 {
// Implementation
}
// In src/feature/tests.rs (all tests)
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_scrape_greenhouse() {
let scraper = GreenhouseScraper::new(vec![]);
let jobs = scraper.scrape().await.unwrap();
assert!(jobs.len() > 0);
}
}Add to mod.rs:
#[cfg(test)]
mod tests;Run tests:
# All tests
cargo test --workspace
# Specific test
cargo test -p jobsentinel-sources test_scrape_greenhouse
# With output
cargo test --workspace -- --nocaptureTest checklist:
- Setup wizard completes successfully
- Job scraping works for all sources
- Scoring algorithm calculates correctly
- Database persists data
- Slack notifications send (if configured)
- Dashboard displays jobs
- Config file loads/saves correctly
- App runs on Windows 11+ and macOS 26+
# Push your branch
git push origin feature/your-feature-name
# Create PR on GitHub
# Include:
# - Clear title and description
# - Link to related issues
# - Screenshots/GIFs (for UI changes)
# - Test resultsBefore submitting:
- Code compiles without errors (
cargo check) - All tests pass (
cargo test) - Code formatted (
cargo fmt,npm run lint:fix) - No linter warnings (
cargo clippy,npm run lint) - Documentation updated (MANDATORY - see table above)
- CHANGELOG.md updated (for ALL significant changes)
- AGENTS.md or
docs/harness/updated (if agent workflow changed) - docs/ROADMAP.md updated (if adding to technical debt)
- Tested on Windows and/or macOS
- No maintainable file-size policy regressions (
npm run lint:bloat)
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Related Issues
Closes #123
## Testing
- [ ] Tested on Windows 11
- [ ] Tested on macOS
- [ ] All Rust tests pass
- [ ] Manual testing completed
## Screenshots
(if applicable)
## Documentation Updates (REQUIRED)
- [ ] CHANGELOG.md updated
- [ ] Feature docs updated (if new feature)
- [ ] AGENTS.md or harness docs updated (if structure/commands changed)
- [ ] ROADMAP.md updated (if technical debt added)
- [ ] N/A - No docs needed (explain why)
## Checklist
- [ ] Code follows project conventions
- [ ] Tests added/updated, with sidecar `tests.rs` files when they keep main logic readable
- [ ] All documentation updated
- [ ] No new warnings
- [ ] No maintainable file-size policy regressions- Confirm the source permits the planned access pattern and document any rate, authentication, or user-consent boundary.
- Add a private adapter under
crates/jobsentinel-sources/src/scrapers/and register it through the scraper facade. Do not expose the adapter module publicly. - Reuse the bounded HTTP and URL-validation owners. Do not create an ad hoc client or bypass redirect, DNS, response-size, or rate-limit controls.
- Extend typed configuration only when the source needs user-controlled
settings. Keep credentials in the approved vault, not
config.json. - Add parser fixtures, error coverage, rate-limit coverage, and a disabled or opt-in live check. Default tests must not contact the source.
- Update source, privacy, security, user, and public-wiki documentation, then run the source and language gates.
Windows 11+, macOS, and Linux are the supported platform owners. Keep target
implementations under crates/jobsentinel-platform/src/, selected by the
private facade with cfg attributes.
- Change the smallest target-specific module and preserve the shared facade.
- Add or update target-independent contract tests for directory, permissions, and error behavior.
- Run the live target check when available. When it is unavailable, use an isolated root or contract test and record the verification gap.
- Update packaging workflows, developer docs, and the verification matrix when target behavior changes.
Adding another supported operating system is a product and release decision, not a local module-only change. It requires packaging, signing, update, security, accessibility, and release evidence before the target is documented as supported.
- Search existing issues
- Try latest version
- Check FAQ
- Keep private job-search details out of public issues.
Use the in-app safe support report when app state matters. It redacts known sensitive values before review and sharing. Do not ask users to paste raw logs, terminal output, stack traces, local paths, resume text, salary floors, notes, or application history into public issues.
**What happened?**
Clear description of what's wrong
**How can we make this happen?**
Steps to reproduce:
1. Go to '...'
2. Click on '...'
3. See problem
**What did you expect?**
What should happen
**Screenshots**
If applicable, and only if they do not show private job-search details.
**Computer**
- OS: [e.g., Windows 11, macOS 26 Tahoe]
- JobSentinel Version: [e.g., 1.0.0]
**Safe support report**
Paste only if you want help and after reviewing it.- Search existing issues
- Check project roadmap
- Keep private job-search details out of public issues.
**What problem does this solve?**
Clear description of the problem
**What would you like to see?**
What you want to happen
**What else have you considered?**
Other solutions you thought about
**Additional context**
Only include details that are safe to make public.- Rust Documentation
- Tauri Documentation
- React Documentation
- TailwindCSS Documentation
- SQLite Documentation
Contributors will be:
- Added to CONTRIBUTORS.md
- Mentioned in release notes
- Given credit in commit history
Thanks for contributing to JobSentinel.
Questions? Open a Discussion