Decision records¶
Every non-obvious structural choice in JobHub is written down as an ADR before it is built.
New records start from 0000-adr-template.md and take the next free
number.
Numbering quirks
The sequence is not perfectly dense. There are two 0009s (ghosted alert and interview
reminders, written in parallel), no 0010, and 0004-test-cases.md is a companion test-case
document rather than a decision. Numbers are never reused or renamed once merged, because
other ADRs, commit messages and tickets link to them.
Grouped by area, newest first within each group.
Crawling and enrichment¶
| ADR | Decision | Status |
|---|---|---|
| 0032 | Shutdown-safe crawler scheduling and trigger-run visibility | Proposed |
| 0029 | Crawl-run visibility: log-level policy, per-target new counts, live progress | Accepted |
| 0021 | Location normalization at crawl-write time | Accepted |
| 0017 | Multiple locations per job post | Accepted |
| 0016 | Crawl until N new posts, not N sources | Accepted |
| 0006 | Cooperative cancellation for admin-triggered passes | Accepted |
| 0004 | Config-driven LLM provider registry for enrichment (test cases) | Proposed |
| 0003 | Admin-only trigger for crawl and enrichment passes | Accepted |
Job search and companies¶
| ADR | Decision | Status |
|---|---|---|
| 0030 | Saved filters are per-user server state; compensation filter leaves the UI | Accepted |
| 0026 | Automatic company enrichment (crawler infers, job-service writes derived) |
Superseded by story #484 |
| 0025 | Admin company enrichment: manual-edit override and admin endpoints | Accepted |
| 0024 | Company logo resolution: job-service derives it | Revised (2026-07-25) |
| 0023 | Company as a first-class entity: schema ownership, identity slug | Accepted |
| 0020 | Facet cache invalidated by a crawl-data generation stamp | Accepted |
| 0018 | Best-effort totals: estimate above a threshold, short-TTL cache | Accepted |
| 0005 | Job-post query performance: indexes and full-text search | Accepted |
| 0001 | Filter-aware (drill-down) facets for GET /jobs/facets |
Accepted |
Identity and security¶
| ADR | Decision | Status |
|---|---|---|
| 0028 | Unconfigured OAuth providers are a first-class state; just-in-time names | Accepted (amended) |
| 0027 | Social login (Google, GitHub) via OAuth authorization-code | Accepted |
| 0022 | Apply-profile answer bank in auth-service | Accepted |
| 0019 | Admin trigger gated by the admin's own 2FA | Accepted |
| 0012 | TOTP two-factor authentication | Accepted |
| 0002 | Email-code account verification (verify before login) | Accepted |
Notifications and applications¶
| ADR | Decision | Status |
|---|---|---|
| 0031 | Notification category derived from notification type | Accepted |
| 0015 | Freeze company logo and name on the application job-post snapshot | Proposed |
| 0014 | Notification cards carry company and job title via enrich-at-read | Accepted |
| 0013 | Resolve the per-application reminders routing collision in the contract | Accepted |
| 0011 | Custom reminders and the channel-gating fix | Accepted |
| 0009 | Interview reminders (24h + 1h): idempotency, internal endpoint, preferences | Proposed |
| 0009 | Ghosted alert via a service-key-authenticated internal status update | Proposed |
| 0008 | Weekly digest email: cross-service communication and design | Proposed |
| 0007 | notification-service: architecture and preferences API | Accepted |
Status is the record's own claim
Several ADRs still read Proposed even though the work shipped: the status field was not
always updated at merge. Treat the code and the architecture overview as
authoritative for what is live, and the ADR as the reasoning behind it.