Merge Request — Wave 0 quick wins + Tier-1/Tier-2 features #1
Reference in New Issue
Block a user
Delete Branch "chore/wave0-quick-wins"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Branch: chore/wave0-quick-wins → main Scope: 24 commits · 62 files · +3,165 / −489 Status: all tests green (backend 135, frontend 23 suites / 54 tests), production build compiles.
Prepared for human review. Do not auto-merge. One operator action is required after merge (DataProtection key rotation — see Known limitations).
Summary
Delivers the first two roadmap tiers plus the engineering-health groundwork, developed as small conventional commits. Two design principles run through it:
Deterministic over "AI-guessy." Match scoring, status suggestions, and pipeline logic are pure/deterministic — instant, reproducible, and safe (the user confirms every state change). This directly answers the market's most common complaint (hallucinated/generic AI output).
One pathway, not two. The bookmarklet and the PWA share-target feed a single /?add= capture flow rather than parallel implementations.
What's included
Engineering health (Wave 0)
security: untracked committed DataProtection keys + daily exports; removed dead legacy controllers.
feat: automated daily SQLite backups (VACUUM INTO, retention, startup catch-up) — prod previously had no automated backup on Linux.
ci: run the entire frontend suite (the old whitelist was hiding 3 broken suites, now fixed).
feat: dev-only OpenAPI at /openapi/v1.json; feat: structured salary fields.
Tier-1 features
Match score (GET /jobapplications/{id}/match-score) — deterministic CV↔job keyword coverage (0–100) + matched/missing keywords + section coverage. Instant panel on the Candidate Fit tab.
Canonical pipeline — JobPipeline single source of truth; status normalized on write (custom values preserved); UI deduped across 5 files; GET .../pipeline.
Analytics v2 — time-in-stage medians (from StatusChanged history) + funnel driven by the pipeline (fixes a bug that omitted the Waiting stage).
Status suggestions — deterministic email→status classifier surfaced as a human-confirmed banner.
Tier-2 features
Bookmarklet quick-capture (Settings) reusing jobimport/preview.
Installable PWA with a mobile share-target into the same capture flow.
Quality
Phase-6 security review (docs/SECURITY_REPORT.md): tenant isolation on new endpoints verified + regression-tested; no injection/ReDoS; dev-only OpenAPI.
Bug fixes: SkillTagger C#/.NET regex (silently missed those skills everywhere), a React stale-closure, a duplicated DB query, and 3 pre-existing hidden test failures.
Test coverage added
New pure/unit-tested services: JobCvMatchService (7), JobPipeline (14), StageAnalytics (4), EmailStatusClassifier (7). New endpoint integration + authorization tests (match-score, status-suggestion). New frontend tests: match-score panel, status-suggestion banner, pipeline, quick-capture, capture-url resolution.
Docs
New: docs/SYSTEM_OVERVIEW.md, docs/PRODUCT_RESEARCH.md, docs/ROADMAP.md, docs/SECURITY_REPORT.md. README updated with the new endpoints, backup/pipeline config, and quick-capture/PWA notes.
Known limitations / follow-ups
ACTION REQUIRED (security): the removed DataProtection key XMLs remain in git history. Rotate them on the production host after merge (see SECURITY_REPORT.md §6).
Per-user custom pipeline stages were deliberately deferred (unproven demand; large surface).
No offline service worker by design — the app deploys frequently and an aggressive cache would risk serving stale builds. The PWA is installable and share-capable without it.
Not yet done (future branches): interview hub (M3), contacts CRM (M4), god-controller decomposition, performance pass, Vite migration.
Reviewer notes
Repo quirk: controllers/services compile via the JobTrackerBackend library, not the JobTrackerApi host project (see docs/SYSTEM_OVERVIEW.md §2).
All AI-adjacent features are deterministic and make no model calls.
New JobCvMatchService: a pure, AI-free keyword-coverage scorer that returns a stable, reproducible 0-100 match score plus matched/missing keyword lists and per-CV-section coverage. Unlike candidate-fit (AI narrative), it makes no model calls, so results are instant and identical for identical inputs - the Jobscan-style differentiator. - GET /api/jobapplications/{id}/match-score - keywords = curated SkillTagger tags (high weight) + salient posting terms (title terms boosted); word-boundary matching avoids false hits - section coverage shows where CV evidence is concentrated - fix(SkillTagger): punctuation-tolerant C#/.NET patterns; the old \b boundaries silently missed 'C#,' and '.NET,' everywhere they are used - 7 unit tests on the pure scorer; full backend suite green (104) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>New EmailStatusClassifier scans a message subject/body for outcome signals (interview invite, offer, rejection) and suggests a canonical pipeline status. Priority-ordered so a rejection that mentions the prior interview still classifies as Rejected. Deterministic - no AI - so it is instant, reproducible, and safe. - GET /api/jobapplications/{id}/status-suggestion reads the job's latest inbound correspondence (incl. Gmail imports) and suggests a forward status move, suppressed when already in/past that stage - always human-confirmed via the existing PATCH .../status - 7 classifier unit tests + 2 endpoint integration tests; backend green (133) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>When a job workspace opens, loads /status-suggestion and shows a dismissible banner when a recent inbound email implies a status move ("This email looks like a move to Interview"). Applying it PATCHes the status; nothing changes without the user's click. - StatusSuggestion type + load-on-open effect + apply handler - warning-toned banner shown above tab content on any tab - EN/NB translations; README endpoint docs - 2 frontend tests; full suite green (21 suites / 48 tests) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>