Skip to content

Latest commit

 

History

History
616 lines (443 loc) · 15.5 KB

File metadata and controls

616 lines (443 loc) · 15.5 KB

Contributing to JobSentinel

Thank you for your interest in contributing to JobSentinel! This guide will help you get started.


For AI Assistants (READ FIRST)

If you're an AI assistant (Claude, GPT, Copilot, etc.) working on this codebase:

  1. Read AGENTS.md.
  2. Read Harness Engineering.
  3. Use Change Contract for non-trivial work.
  4. Run sensors from Verification Matrix.
  5. Update docs when behavior, setup, architecture, commands, or security changes.

Documentation Updates

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?"

File Size Policy

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 Current Status

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.


Table of Contents


Code of Conduct

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

Project Ethos

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.


Getting Started

Prerequisites

Required:

Platform-Specific:

  • Windows: Visual Studio Build Tools 2022, Windows 10 SDK
  • macOS: Xcode Command Line Tools
  • Linux: GTK development libraries

Fork and Clone

# 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

Install Dependencies

# Synchronize locked dependencies and prove the baseline
./init.sh

# Check Rust compilation
cargo check --workspace

Run Development Mode

# Start the app with hot reload
npm run tauri:dev

Development Workflow

1. Sync with Upstream

# 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 main

2. Make Changes

Follow the project structure:

  • src/ - React frontend (TypeScript + TailwindCSS)
  • crates/jobsentinel-*/src/ - Platform-agnostic code by bounded owner
  • crates/jobsentinel-platform/src/ - OS-specific code
  • src-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

3. Test Your Changes

# Run Rust tests
cargo test --workspace

# Run with logging
RUST_LOG=debug npm run tauri:dev

# Test on target platforms
# - Windows 11+
# - macOS 26+

4. Commit Changes

# 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 feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code formatting (no logic changes)
  • refactor: Code restructuring
  • test: Adding/updating tests
  • chore: Maintenance tasks

Coding Standards

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.

Rust Code

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 sources

Best 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 (not println!)
  • 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...
}

TypeScript/React Code

Follow React best practices:

# Lint and auto-fix code
npm run lint:fix

# Lint code (check only)
npm run lint

Best 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>
  );
}

Testing

Rust Tests

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 -- --nocapture

Manual Testing

Test 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+

Submitting Changes

Create Pull Request

# 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 results

PR Checklist

Before 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)

PR Template

## 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

Adding New Features

Adding a New Job Source

  1. Confirm the source permits the planned access pattern and document any rate, authentication, or user-consent boundary.
  2. Add a private adapter under crates/jobsentinel-sources/src/scrapers/ and register it through the scraper facade. Do not expose the adapter module publicly.
  3. 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.
  4. Extend typed configuration only when the source needs user-controlled settings. Keep credentials in the approved vault, not config.json.
  5. Add parser fixtures, error coverage, rate-limit coverage, and a disabled or opt-in live check. Default tests must not contact the source.
  6. Update source, privacy, security, user, and public-wiki documentation, then run the source and language gates.

Changing a Platform Adapter

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.

  1. Change the smallest target-specific module and preserve the shared facade.
  2. Add or update target-independent contract tests for directory, permissions, and error behavior.
  3. Run the live target check when available. When it is unavailable, use an isolated root or contract test and record the verification gap.
  4. 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.


Reporting Problems

Before Reporting A Problem

  1. Search existing issues
  2. Try latest version
  3. Check FAQ
  4. 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.

Problem Report Template

**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.

Improvement Ideas

Before Suggesting An Improvement

  1. Search existing issues
  2. Check project roadmap
  3. Keep private job-search details out of public issues.

Improvement Template

**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.

Resources


Recognition

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