docs: add CONTRIBUTING.md codifying the project philosophy (#80)

Codifies what has so far lived in PR closing comments: the universal-
template rule, merge/decline criteria with precedent links, the
feature-complete command bar, the empirical-verification standard,
and the fork path via /add-portal and the community forks discussion.

GitHub auto-surfaces CONTRIBUTING.md on new PRs and issues; README
gets a one-line pointer for browsers.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mads Lorentzen
2026-07-08 21:11:03 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent fc9e3e1f32
commit d4415dff16
2 changed files with 55 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
# Contributing
Thanks for considering a contribution! This repo has a deliberate, narrow philosophy, and most declined PRs are well-executed work that simply didn't know about it. Read this first; it will save you effort and tell you where your work will land best.
## The one rule everything follows from
**This repo is a universal template.** People fork it and adapt it to their own market, language, and profile. Upstream stays market-agnostic, person-agnostic, and Claude Code-native. The corollary: a contribution is judged by fit to this rule first, execution quality second. Well-built but off-policy still gets declined (kindly, with reasons).
## What gets merged
- **Universal customization features**: anything that makes the fork-and-adapt path better for everyone. Precedent: `/add-template` (#30), `/add-portal` (#37).
- **Robustness and correctness fixes** with the failing case demonstrated. Precedent: NaN flag validation (#35), HTML entity decoding (#55, #56), salary column detection (#64).
- **Docs that close real gaps**: platform-specific setup (#41, #60), stale references (#36, #68).
- **Infrastructure that reduces review burden** and is argued from evidence, not speculation. Precedent: CI (#59), which caught a latent bug while being built.
## What gets declined
- **Market- or country-specific skills and content.** One country's portal opens the door to every country's portal; there is no principled stopping point. Precedent: #31 (India), #39 (France, despite an honest and excellent PR), #67 (China). The in-tree portal skills are either country-agnostic (`linkedin-search`) or the maintainer's own demonstration instance (the Danish portals).
- **Personal profile data.** The template ships placeholders; your populated profile lives in your fork. CI enforces this (`placeholder-integrity`). Precedent: #17, #72.
- **Alternative-harness ports and duplicate workflow sources.** The markdown specs ARE the implementation; a second copy (another agent CLI, an orchestration layer, a wrapper command) drifts from the first the moment either changes. Precedent: #44, #49, #66.
- **Speculative infrastructure.** Complexity must be argued from a problem that exists, not one that might. Precedent: #63.
- **Kitchen-sink PRs.** One concern per PR. Bundles get asked to split (#73) - and splits get reviewed fast (#75, #76 arrived within the hour and were handled same-day).
## The bar for new commands
The core lifecycle is **feature-complete**: `/setup``/scrape``/rank``/apply``/interview``/outcome` → calibration back into `/setup`, with `/expand`, `/upskill`, `/add-template`, `/add-portal`, and `/reset` around it. Every stage of a real job hunt has an owner.
A new command therefore faces a high bar. The test that admitted the existing ones: **does it operationalize something error-prone that already exists in the framework** (documented machinery nothing executes, data something writes but nothing reads)? "Useful" and "possible" are not sufficient; the strongest proposals connect two things that already exist without modifying either (#43, #54).
## Claims get verified
Reviews here are empirical. Bug reports are reproduced on master before the fix is considered; "all tests green" is checked against whether the tests can distinguish master from the fix. PRs whose premise doesn't reproduce get declined even when the code is fine - it has happened (#35's converter fix, #52's first version). You can make this fast:
- State the failing case and how to reproduce it.
- Put CLI tests in `.agents/skills/<name>/cli/tests/` (bun test, network-free where possible); Python tool tests in `tests/`.
- Run what CI runs: `python tools/lint_skills.py`, `bun run typecheck` in touched CLIs, and the relevant test suites.
## Building for your own market? Do this instead
1. Fork the repo and run `/add-portal` with your local job board - it scaffolds a portal skill matching the shipped contract, and `/scrape` picks it up automatically.
2. Announce your fork in the pinned [Community forks & adaptations](https://github.com/MadsLorentzen/ai-job-search/discussions/78) discussion so others can find it.
Market-specific skills are genuinely valuable - they just live in forks, where their maintainers can test them and their users can find them.
## Practical notes
- **Portal-skill contract**: `search`/`detail` commands, `--format json|table|plain`, stderr JSON errors with exit 1, backoff on 429/5xx, zero runtime dependencies by default. See `/add-portal`'s spec and `linkedin-search` as the reference implementation.
- **Personal-use boundaries**: portal skills that touch ToS-restricted sources carry a prominent personal-use-only warning, and CI deliberately makes no live portal requests. Don't "fix" that.
- **LaTeX changes**: both templates must compile (`lualatex` for the CV, `xelatex` for the cover letter) and hold their exact page counts. CI smoke-checks this.
Questions and proposals are welcome in [Discussions](https://github.com/MadsLorentzen/ai-job-search/discussions) - an Idea thread costs nothing and can save you building the wrong thing :-)
+4
View File
@@ -309,6 +309,10 @@ The framework supports two distinct modes of job searching:
To get the most from this, invest time during `/setup` in describing not just your experience, but what energized you, what drained you, and what you'd want more of. This context directly shapes how the system evaluates fit and which roles it surfaces during `/scrape`.
## Contributing
Thinking about a PR? Read [CONTRIBUTING.md](CONTRIBUTING.md) first - it explains what gets merged, what lives in forks, and why.
## Acknowledgements
- [Mikkel Krogholm](https://github.com/mikkelkrogsholm) ([skills repo](https://github.com/mikkelkrogsholm/skills)) for the job search CLI skills