docs: reorganize tree, restore architecture + research from archive, add Phase 0 reports
Active docs/ was stub scaffolding while the real docs sat in docs/_archive/. Restore and correct them, and record the Phase 0 work. - docs/architecture/current.md: verified system map (from archived SYSTEM_OVERVIEW, 9 corrections against code). - docs/research/competitors.md: sourced competitor analysis (from archived PRODUCT_RESEARCH, feature matrix corrected). - docs/decisions/ADR-002-job-application-model.md: the Job/JobApplication split. - docs/application-discovery-report.md, docs/implementation-roadmap.md, docs/phase-0-foundation-report.md, docs/career-workspace-branch-assessment.md. - Remove 10 zero-byte placeholder files that advertised content that never existed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# Jobbjakt Architecture Evolution
|
||||
|
||||
## Guiding constraints
|
||||
|
||||
- Preserve the existing single-master-profile model.
|
||||
- Keep job applications and their workflow at the centre of the domain.
|
||||
- Make AI providers replaceable and optional.
|
||||
- Add SaaS foundations without prematurely building a billing platform or team product.
|
||||
- Keep feature slices small while reducing the current controller concentration.
|
||||
|
||||
## Target boundaries
|
||||
|
||||
```text
|
||||
Web UI
|
||||
-> Application APIs
|
||||
-> Application workflow domain
|
||||
-> Career profile and document domain
|
||||
-> Discovery provider adapters
|
||||
-> Connected-account adapters
|
||||
-> AI provider boundary
|
||||
-> Entitlements and usage policy
|
||||
```
|
||||
|
||||
### Career profile and documents
|
||||
|
||||
`ApplicationUser.ProfileCvStructureJson` remains the canonical master profile during the transition. A later migration may normalize high-change sections (projects and portfolio items) into user-owned tables, but generated CVs and application-specific drafts must remain immutable copies with a profile-version reference.
|
||||
|
||||
### Discovery
|
||||
|
||||
Create an `IJobDiscoveryProvider` contract with normalized query and result types. Providers own authentication, source-specific filters, terms-aware attribution, and mapping. The UI and import flow must not know provider payload shapes. Cache public results briefly, do not persist an external job until the user imports it.
|
||||
|
||||
### AI
|
||||
|
||||
Extract AI prompts and model transport from controllers behind task-focused interfaces: CV extraction, CV tailoring, draft generation, interview preparation, and summarization. Each request records provider, model, input profile version, job context, and user review state. Local and online providers implement the same boundary.
|
||||
|
||||
### Entitlements
|
||||
|
||||
Introduce account-level plan, entitlement, and usage-ledger records before payment integration. Product code checks named capabilities (for example `ai.tailor_cv`, `cv.premium_theme`, `storage.attachments`) rather than payment-provider state. This supports trials, manual access, subscriptions, and future organisation plans.
|
||||
|
||||
## Incremental engineering work
|
||||
|
||||
1. Add targeted feature services beside the existing controllers for every touched feature; do not attempt a big-bang controller rewrite.
|
||||
2. Replace local-only preferences that affect behaviour with user-owned persisted settings.
|
||||
3. Replace the in-memory CV queue with a database-backed work item before horizontal hosting.
|
||||
4. Use PostgreSQL for hosted multi-tenant deployment; retain SQLite for self-hosted/single-user deployments.
|
||||
5. Add provider health, job processing, AI usage, and entitlement audit telemetry before paid hosting.
|
||||
|
||||
## Explicit non-goals for the first SaaS release
|
||||
|
||||
- Teams/organisations and shared candidate data.
|
||||
- Auto-apply automation or scraping-proxy infrastructure.
|
||||
- A browser-extension marketplace release.
|
||||
- A billing-provider integration before commercial policy is decided.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Merge Request — Wave 0 quick wins + Tier-1/Tier-2 features
|
||||
|
||||
**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:
|
||||
|
||||
1. **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).
|
||||
2. **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.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Jobbjakt Product Evolution Plan
|
||||
|
||||
> Confirmed 2026-07-15. This document is the product decision record for the career-management evolution.
|
||||
|
||||
## Product position
|
||||
|
||||
Jobbjakt is a job-tracking and career-management product. The application workflow remains the core loop:
|
||||
|
||||
```text
|
||||
Discover opportunity -> Track application -> Prepare application -> Follow up -> Learn and progress
|
||||
```
|
||||
|
||||
Career profile, CV generation, cover letters, portfolio, interview preparation, and discovery support this loop. They must not displace it or turn Jobbjakt into a CV-builder-first product.
|
||||
|
||||
## Confirmed decisions
|
||||
|
||||
| Decision | Direction |
|
||||
| --- | --- |
|
||||
| Primary discovery market | Nordics and Europe; global later |
|
||||
| Discovery sources | Permitted provider APIs and existing URL capture; no scraping or browser extension in the first release |
|
||||
| Career workspace | Dedicated top-level navigation item |
|
||||
| Add-job flow | Wizard; create the application after job review, then keep preparation steps optional and resumable |
|
||||
| Portfolio | Lightweight links and project items connected to the master career profile |
|
||||
| AI | Retain local and online capability behind a provider boundary and explicit user controls |
|
||||
| SaaS priority | Hosted subscriptions and feature entitlements before organisations/teams |
|
||||
| Brand | Keep the Jobbjakt name |
|
||||
|
||||
## Target information architecture
|
||||
|
||||
| Area | Purpose | Initial destinations |
|
||||
| --- | --- | --- |
|
||||
| Dashboard | Daily direction | priorities, follow-ups, recent activity |
|
||||
| Applications | Core tracker | list, board, companies, application workspace |
|
||||
| Career Workspace | Reusable career source of truth | master profile, CV builder, portfolio, interview preparation |
|
||||
| Discover Jobs | Find then act | provider search, saved searches later, import into tracker |
|
||||
| Settings | Product preferences | general, follow-ups, notifications, connected accounts, data |
|
||||
| Account | Identity and security | personal details, password, providers, sessions, 2FA |
|
||||
|
||||
## Delivery roadmap
|
||||
|
||||
### Phase 1 — Workflow clarity
|
||||
|
||||
1. Create the Career Workspace and move CV-facing functionality out of Account Profile.
|
||||
2. Move Gmail, Outlook, and IMAP management to Settings > Connected Accounts.
|
||||
3. Reorganise Settings: remove normal-user export/import controls, hide admin-only status controls, and improve follow-up explanations.
|
||||
4. Simplify application workspace navigation into task-oriented sections.
|
||||
|
||||
### Phase 2 — Application preparation
|
||||
|
||||
1. Replace the dense Add Job modal with a six-step wizard.
|
||||
2. URL capture and extraction lead the flow; manual details remain an easy fallback.
|
||||
3. Save the application after review; CV, cover letter, portfolio, and documents are explicitly optional.
|
||||
4. Link job-specific CV copies and draft material to the application without mutating the master profile.
|
||||
|
||||
### Phase 3 — Career workspace completion
|
||||
|
||||
1. Strengthen the structured master career profile: experience, education, skills, projects, certifications, languages, achievements, and portfolio links.
|
||||
2. Turn the existing template system into a guided three-step CV builder: theme, preview/customise, save defaults.
|
||||
3. Keep five professional templates and persisted visual preferences; add drag-and-drop ordering after the underlying configuration is durable.
|
||||
|
||||
### Phase 4 — Nordic/European discovery
|
||||
|
||||
1. Introduce a provider-adapter contract and a normalized result model.
|
||||
2. Implement approved public/provider APIs beginning with Nordic sources; add European sources only when their terms permit product use.
|
||||
3. Provide search, filters, source attribution, map/distance enrichment where data permits, and one-click import to the wizard.
|
||||
4. Add fuzzy role matching locally through a role/skill synonym catalog before any external AI ranking.
|
||||
|
||||
### Phase 5 — Hosted SaaS foundations
|
||||
|
||||
1. Add account-level entitlement and usage records independent of a billing provider.
|
||||
2. Gate premium themes, AI allowances, storage, and discovery quotas through policy checks.
|
||||
3. Integrate a billing provider only after pricing, tax, trial, cancellation, and support policy decisions are recorded.
|
||||
4. Move restart-sensitive CV processing to durable storage before multi-instance hosting.
|
||||
|
||||
## Success measures
|
||||
|
||||
- A new user can create a tracked application from a URL in under two minutes.
|
||||
- A user can find their master profile, create a CV, and manage email connections without visiting Account Profile.
|
||||
- Optional preparation never blocks capture of an opportunity.
|
||||
- Every AI-generated output visibly states its source profile/job context and requires user review before it is saved or sent.
|
||||
- Discovery always preserves source attribution and sends users to the original job advert to apply.
|
||||
@@ -0,0 +1,121 @@
|
||||
# PRODUCT_RESEARCH.md — Job Application Tracking Market (2026)
|
||||
|
||||
> Phase 2 deliverable. Research conducted 2026-07-02 via web sources (linked throughout).
|
||||
> Purpose: position Jobbjakt against the market and rank the features worth building next.
|
||||
|
||||
---
|
||||
|
||||
## 1. Market landscape
|
||||
|
||||
The market splits into five clusters:
|
||||
|
||||
| Cluster | Representatives | Model |
|
||||
|---|---|---|
|
||||
| **Tracker-first + AI resume** | [Teal](https://www.tealhq.com/), [Huntr](https://huntr.co/pricing), JibberJobber | Freemium SaaS; premium $29–40/mo |
|
||||
| **Autofill / volume** | [Simplify](https://simplify.jobs/job-application-tracker) (autofill), [LazyApply](https://lazyapply.com/) ($99–999/yr), LoopCV (auto-apply) | Extension-centric |
|
||||
| **Matching + copilot** | [Jobright](https://jobright.ai/blog/teal-review-2026-walkthrough-alternatives-and-faqs/) | AI job matching, resume tailoring, autofill |
|
||||
| **Resume/ATS optimization** | [Jobscan](https://www.jobscan.co/) ($49.95/mo!), Resume Worded, Rezi | Match-score per job description |
|
||||
| **Self-hosted / privacy** | [JobSync](https://github.com/Gsync/jobsync), [CareerSync](https://github.com/Tomiwajin/CareerSync), [career-ops](https://career-ops.org/), various [GitHub projects](https://github.com/topics/job-application-tracker) | OSS, local-first, often Ollama-based |
|
||||
| **Email auto-tracking** | [Trackr](https://www.trackrjobs.com/), [G-Track](https://jobtrack-ai.com/gmail-job-tracker), Gmail [Chrome extensions](https://chromewebstore.google.com/detail/gmail-job-application-tra/lkpjngmdfncejiomkofogfdoppgifmkh) | Inbox scanning → status updates |
|
||||
|
||||
### Competitor snapshots
|
||||
|
||||
**Teal** — market leader for tracker+resume. Free: unlimited tracking, Chrome extension (50+ job boards), kanban (Saved/Applied/Interview/Offer/Rejected), 10 ATS templates, contact manager, ATS score (15 checks). Premium ($9/wk, $29/mo, [$79/qtr](https://www.tealhq.com/pricing)): keyword match scoring, AI bullets/cover letters, analytics. Cons reported: [billing-after-cancellation complaints, generic/hallucinating AI content, ATS failures on two-column templates](https://resumehog.com/blog/posts/teal-hq-review-april-2026-is-the-job-tracker-worth-your-time.html), [high-maintenance workflow, overwhelming UI, poor support](https://resumejudge.com/blog/tealhq-review/), no automation.
|
||||
|
||||
**Huntr** — best visual kanban + CRM layer. Free: 100 tracked jobs cap, unlimited base resumes, basic scoring. [Pro $40/mo](https://huntr.co/pricing): AI tailored resumes, unlimited cover letters, advanced matching/insights. 4.9★ extension (clip from any site + autofill). Cons: [must rebuild resume inside their builder, plain templates, free plan stops being useful fast](https://resumejudge.com/blog/huntr-review/), online-only.
|
||||
|
||||
**Simplify** — free autofill extension for 100+ ATS portals (Workday, Greenhouse, iCIMS), real-time keyword flagging, pipeline tracking. Execution-focused, light on CRM depth.
|
||||
|
||||
**Jobscan** — per-job resume match score (1–100, 30+ checks, "aim ≥75%"), cover-letter optimization report. Expensive ($49.95/mo). This single feature is the most-cited reason people pay for job-search tools.
|
||||
|
||||
**Email auto-trackers** (Trackr, G-Track, extensions) — scan Gmail, AI-classify (Applied/Next step/Rejected/Offer), auto-update statuses, apply labels. This is rapidly becoming table stakes; users love "zero manual data entry".
|
||||
|
||||
**Self-hosted OSS** (JobSync, CareerSync, career-ops) — privacy pitch ("no cloud, no telemetry, no account"), Ollama/local-LLM parsing, but all are far less complete than Jobbjakt: mostly CRUD + basic AI, no CV pipeline, no correspondence CRM, no rules engine.
|
||||
|
||||
### Standard vs premium features across the market
|
||||
|
||||
- **Table stakes (free everywhere):** kanban board, status stages, notes, basic contact tracking, browser clipper, export.
|
||||
- **Premium (what people pay for):** per-job resume↔JD **match scoring with keyword gaps**, AI tailored resumes/cover letters, analytics (response rate, funnel conversion, time-in-stage), email/interview follow-up automation, autofill at scale.
|
||||
- **Emerging differentiators:** inbox auto-tracking, interview prep hubs (question banks, scheduling, calendar sync — cf. [interview scheduling tools](https://www.selectsoftwarereviews.com/buyer-guide/interview-scheduling-software)), job-match scoring against a profile, salary/offer comparison.
|
||||
|
||||
### Recurring user frustrations (opportunities)
|
||||
|
||||
1. **Privacy/data anxiety** — sensitive career data on VC-funded SaaS; [breach/misuse concerns](https://www.saashub.com/compare-job-tracker-by-teal-vs-huntr). Jobbjakt's core moat.
|
||||
2. **Paywall fatigue** — free tiers cap exactly at the point of seriousness (Huntr's 100 jobs, Teal's AI credits, Jobscan's 5 scans/mo).
|
||||
3. **AI slop** — hallucinated skills, misspelled names, generic bullets; users want AI grounded in *their* real CV (Jobbjakt's structured-CV grounding is the right architecture).
|
||||
4. **Manual data entry** — retyping jobs and statuses; solved by clippers + inbox scanning.
|
||||
5. **Vendor lock-in** — resumes trapped in proprietary builders (Huntr), hard exports.
|
||||
6. **Tool sprawl** — tracker + Jobscan + resume builder + calendar = 4 subscriptions; users want one hub.
|
||||
|
||||
---
|
||||
|
||||
## 2. Feature matrix — Jobbjakt vs market
|
||||
|
||||
✅ has it · 🟡 partial · ❌ missing
|
||||
|
||||
| Feature | Teal | Huntr | Simplify | OSS self-hosted | **Jobbjakt today** |
|
||||
|---|---|---|---|---|---|
|
||||
| Kanban pipeline | ✅ | ✅ | ✅ | 🟡 | 🟡 board view exists; status is free-text, no drag-drop canonical pipeline |
|
||||
| Job capture from URL | ✅ ext | ✅ ext | ✅ ext | 🟡 | 🟡 server-side parse (Finn/NAV/LinkedIn/Jobbnorge + JSON-LD); no extension/bookmarklet |
|
||||
| Inbox auto-tracking | ❌ | ❌ | 🟡 | 🟡 | ✅ **Gmail OAuth import + human review queue** (ahead of paid SaaS) |
|
||||
| Contacts/recruiter CRM | ✅ | ✅ | ❌ | ❌ | 🟡 company-level only, no people entities |
|
||||
| Resume/CV builder | ✅ | ✅ | 🟡 | ❌ | ✅ structured CV parse + templates + PDF export |
|
||||
| Per-job tailored resume (AI) | 💰 | 💰 | 💰 | ❌ | ✅ **local-AI tailored drafts** (privacy-unique) |
|
||||
| Resume↔JD match score + keyword gaps | 💰 | 💰 | 🟡 | ❌ | ❌ (handoff doc lists "missing-keyword analysis" as planned) |
|
||||
| AI cover letters / messages | 💰 | 💰 | 💰 | ❌ | ✅ free, local |
|
||||
| Follow-up reminders | ✅ | ✅ | 🟡 | ❌ | ✅ + rules engine (auto-ghost) — richer than most |
|
||||
| Analytics dashboard (funnel, response rate, time-in-stage) | 💰 | 💰 | 🟡 | 🟡 | 🟡 basic stats endpoint only |
|
||||
| Interview management (schedule, prep notes, calendar) | 🟡 | 🟡 | ❌ | ❌ | ❌ (only generic follow-up dates) |
|
||||
| Calendar integration (ICS/Google) | 🟡 | 🟡 | ❌ | ❌ | ❌ |
|
||||
| Salary/offer tracking & comparison | 🟡 | 🟡 | ❌ | ❌ | 🟡 salary text field only |
|
||||
| Autofill applications | ❌ | ✅ | ✅ | ❌ | ❌ (out of scope — needs extension) |
|
||||
| Multi-language (EN/NB) + translation | ❌ | ❌ | ❌ | ❌ | ✅ unique for Nordic market |
|
||||
| Self-hosted / data ownership | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| Mobile experience | ✅ apps | ✅ | ✅ | ❌ | 🟡 responsive-ish desktop web; no PWA |
|
||||
| Export/portability | 🟡 | 🟡 | 🟡 | ✅ | ✅ JSON/CSV + daily export |
|
||||
|
||||
**Position:** Jobbjakt is already **ahead of every OSS competitor** and matches or beats paid SaaS on AI drafting, Gmail import, and data ownership. Its gaps versus paid SaaS are: match scoring, canonical pipeline/kanban UX, interview & calendar layer, analytics depth, capture friction (no extension), and contact-level CRM.
|
||||
|
||||
---
|
||||
|
||||
## 3. Market gap — what would make Jobbjakt significantly better than existing solutions
|
||||
|
||||
> **"The private, self-hosted career hub: everything Teal+Huntr+Jobscan charge $70–90/mo for, powered by your own local AI, with your data never leaving your server."**
|
||||
|
||||
No product today combines: serious tracker UX + inbox auto-tracking + local-LLM tailoring + match scoring + interview hub, self-hosted. Jobbjakt is uniquely ~60% of the way there.
|
||||
|
||||
---
|
||||
|
||||
## 4. Ranked feature ideas (value × effort)
|
||||
|
||||
Effort: S (<1 day) · M (1–3 days) · L (1–2 wk) · XL (>2 wk). Grounded in the Phase 1 codebase map.
|
||||
|
||||
| # | Feature | User impact | Effort | Notes |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **CV↔job match score + keyword gap analysis** (per job: score, missing keywords, section coverage; reuse structured CV JSON + existing Ollama path) | ★★★★★ — the #1 paid feature in the market, free & local here | M–L | Backend has all inputs already; add endpoint + UI panel in job workspace |
|
||||
| 2 | **Canonical pipeline + drag-drop kanban** (status enum/ordering, custom stages per user, drive board/badges from it) | ★★★★★ — core daily UX; free-text status blocks analytics too | M–L | Already on README wish list; needs migration for status normalization |
|
||||
| 3 | **Analytics dashboard v2** (funnel conversion, response rate, time-in-stage, weekly activity, source effectiveness) | ★★★★ — retention feature; needs #2 for clean stages | M | Data all exists in `JobEvent` history |
|
||||
| 4 | **Interview hub** (interview entity: rounds, type, scheduled time, prep notes, outcome; ICS feed/export + reminders) | ★★★★ — biggest functional gap vs SaaS | L | New entity + timeline integration; ICS is cheap, Google Calendar sync later |
|
||||
| 5 | **Bookmarklet / minimal browser capture** (one-click "save to Jobbjakt" using existing `jobimport/preview`) | ★★★★ — kills the biggest friction (manual entry); full extension can wait | S–M | Server parsing already exists; a bookmarklet or share-target PWA is days not weeks |
|
||||
| 6 | **Contacts (people) CRM** (recruiter/hiring-manager entities linked to companies/jobs/correspondence) | ★★★ | M | Natural extension of company recruiter fields |
|
||||
| 7 | **PWA pass** (installable, mobile nav polish, share-target for job URLs) | ★★★ — mobile is where users check status | M | CRA supports PWA manifest; pairs with #5 |
|
||||
| 8 | **Salary/offer tracker** (structured salary min/max/currency, offer comparison view) | ★★ | S–M | Currently a free-text field |
|
||||
| 9 | **Smarter inbox** (extend existing Gmail review with AI status suggestions: "this looks like a rejection → move to Rejected?") | ★★★★ — compounds an existing unique strength | M | Classification via existing Ollama service |
|
||||
| 10 | **Web push / digest notifications** (beyond SMTP) | ★★ | M | Needs service worker (pairs with #7) |
|
||||
|
||||
Deliberately **not** recommended: auto-apply bots (ToS/ethics/quality problems, LazyApply-style tools are poorly reviewed), building a full Chrome-store extension now (high maintenance; bookmarklet first), multi-provider cloud AI (undermines the privacy moat — keep local-first with optional cloud later).
|
||||
|
||||
## 5. Recommended implementation order (input to Phase 3 roadmap)
|
||||
|
||||
1. **Match score + keyword gaps** (#1) — flagship differentiator, builds on freshest code (structured CV).
|
||||
2. **Canonical pipeline + kanban** (#2) — unblocks analytics, fixes daily UX.
|
||||
3. **Analytics v2** (#3) — quick follow-on.
|
||||
4. **Bookmarklet capture** (#5) + **PWA** (#7) — friction killers.
|
||||
5. **Interview hub** (#4) — biggest new surface, schedule after the above land.
|
||||
6. Then #9, #6, #8, #10 by appetite.
|
||||
|
||||
Engineering-health work (CI test whitelist, prod DB backups, god-controller decomposition) is tracked separately in `docs/SYSTEM_OVERVIEW.md` §15–17 and should interleave with feature work in Phase 3.
|
||||
|
||||
---
|
||||
|
||||
Sources: [Prentus tracker roundup](https://prentus.com/blog/we-found-the-5-best-job-tracker-tools-on-the-market) · [ApplyArc comparison](https://applyarc.com/compare/best-job-application-trackers) · [Teal pricing](https://www.tealhq.com/pricing) · [Teal reviews (ResumeHog)](https://resumehog.com/blog/posts/teal-hq-review-april-2026-is-the-job-tracker-worth-your-time.html) · [Teal cons (ResumeJudge)](https://resumejudge.com/blog/tealhq-review/) · [Huntr pricing](https://huntr.co/pricing) · [Huntr cons (ResumeJudge)](https://resumejudge.com/blog/huntr-review/) · [Huntr vs Teal](https://huntr.co/blog/huntr-vs-teal) · [Simplify tracker](https://simplify.jobs/job-application-tracker) · [Jobright review of Teal](https://jobright.ai/blog/teal-review-2026-walkthrough-alternatives-and-faqs/) · [LazyApply](https://lazyapply.com/) · [Auto-apply tools compared](https://blog.fastapply.co/auto-apply-jobs-tools-compared-2026) · [Jobscan](https://www.jobscan.co/) · [Jobscan pricing](https://onlineatschecker.com/blog/jobscan-pricing-2026-free-plan-worth-it) · [JobSync (OSS)](https://github.com/Gsync/jobsync) · [CareerSync (OSS)](https://github.com/Tomiwajin/CareerSync) · [career-ops](https://career-ops.org/) · [Trackr](https://www.trackrjobs.com/) · [G-Track](https://jobtrack-ai.com/gmail-job-tracker) · [Gmail tracker extension](https://chromewebstore.google.com/detail/gmail-job-application-tra/lkpjngmdfncejiomkofogfdoppgifmkh) · [Interview scheduling software guide](https://www.selectsoftwarereviews.com/buyer-guide/interview-scheduling-software) · [SaaSHub Teal vs Huntr](https://www.saashub.com/compare-job-tracker-by-teal-vs-huntr)
|
||||
@@ -0,0 +1,75 @@
|
||||
# ROADMAP.md — Jobbjakt Product & Engineering Roadmap
|
||||
|
||||
> Phase 3 deliverable (2026-07-02). Sources: `docs/SYSTEM_OVERVIEW.md` (Phase 1) and `docs/PRODUCT_RESEARCH.md` (Phase 2).
|
||||
> Scoring: Value/Complexity/Risk on ▲ high / ● medium / ▽ low. Effort: S <1 day · M 1–3 days · L 1–2 wk · XL >2 wk.
|
||||
|
||||
**North star:** the private, self-hosted career hub — the tracker UX of Huntr, the tailoring/scoring of Teal+Jobscan, powered by local AI, with data that never leaves your server.
|
||||
|
||||
---
|
||||
|
||||
## Tier 0 — Quick Wins (do first; days, low risk, compounding payoff)
|
||||
|
||||
| # | Item | Type | Value | Effort | Risk | Rationale |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Q1 | **CI: run the full frontend test suite** (replace the hand-maintained 10-file whitelist with the whole suite; fix/quarantine any flaky test explicitly) | eng | ▲ | S | ▽ | New tests currently silently skipped in CI; already caused a gap once |
|
||||
| Q2 | **Automated production DB backup** (scheduled SQLite `VACUUM INTO`/copy to `exports/` with retention; document restore) | eng | ▲ | S–M | ▽ | Prod currently has *no working automated backup* (backup endpoint is Windows-DPAPI-only, prod is Linux) |
|
||||
| Q3 | **Repo hygiene** (delete dead root `Controller/`; remove `temp_job.json`, `temp_post_job.py`; gitignore `JobTrackerApi/CvArtifacts/`, `bin_build/`, stray artifacts; commit pending WIP fixes on a branch) | eng | ● | S | ▽ | Removes footguns before refactors; working tree currently dirty |
|
||||
| Q4 | **Swagger/OpenAPI** (Swashbuckle or built-in OpenAPI, dev-only exposure) | eng | ● | S | ▽ | README endpoint list already drifts; prerequisite for a generated TS client later |
|
||||
| Q5 | **Structured salary fields** (min/max/currency/period alongside the free-text field, backfill-friendly) | product | ● | S–M | ▽ | Cheap now, prerequisite for offer comparison + analytics later |
|
||||
|
||||
## Tier 1 — High Value (the differentiators; next 2–4 weeks of feature work)
|
||||
|
||||
| # | Item | Value | Effort | Risk | Notes |
|
||||
|---|---|---|---|---|---|
|
||||
| H1 | **CV↔Job match score + keyword gap analysis** — per-job score, missing keywords, section coverage; reuse `ProfileCvStructureJson` + existing Ollama path; panel in job workspace | ▲▲ | M–L | ● | The market's #1 paid feature (Jobscan $50/mo), free & local here. Flagship differentiator |
|
||||
| H2 | **Canonical pipeline + drag-drop kanban** — status enum + ordering + per-user custom stages; migration normalizing existing free-text statuses; board becomes drag-drop | ▲▲ | M–L | ● | Fixes daily UX; unblocks H3; the riskiest part is the status migration (needs careful mapping + tests) |
|
||||
| H3 | **Analytics dashboard v2** — funnel conversion, response rate, time-in-stage, weekly activity, source effectiveness (data already in `JobEvent`) | ▲ | M | ▽ | Depends on H2 for clean stages |
|
||||
| H4 | **Gmail AI status suggestions** — extend the existing review queue: classify incoming mail (rejection/interview/offer) via local AI and suggest status moves, human-confirmed | ▲ | M | ● | Compounds an existing unique strength; keep human-in-the-loop |
|
||||
|
||||
## Tier 2 — Medium Value (after Tier 1)
|
||||
|
||||
| # | Item | Value | Effort | Risk |
|
||||
|---|---|---|---|---|
|
||||
| M1 | **Bookmarklet / PWA share-target capture** — one-click save-to-Jobbjakt reusing `jobimport/preview` | ▲ | S–M | ▽ |
|
||||
| M2 | **PWA pass** — manifest, installability, mobile nav polish | ● | M | ▽ |
|
||||
| M3 | **Interview hub** — interview entity (round, type, time, prep notes, outcome), timeline integration, ICS export + reminders | ▲ | L | ● |
|
||||
| M4 | **Contacts (people) CRM** — recruiter/hiring-manager entities linked to companies/jobs/correspondence | ● | M | ▽ |
|
||||
| M5 | **Durable CV processing queue** — DB-backed queue replacing in-memory (jobs survive restart) | ● | M | ● |
|
||||
| M6 | **ProblemDetails + validation consistency** across API | ● | M | ▽ |
|
||||
|
||||
## Tier 3 — Long-Term Improvements (structural; interleave carefully)
|
||||
|
||||
| # | Item | Value | Effort | Risk |
|
||||
|---|---|---|---|---|
|
||||
| L1 | **Decompose god controllers** (`JobApplicationsController` 151 KB, `ProfileCvController` 117 KB, `GmailController` 60 KB) into feature services; extract AI prompt construction behind interfaces. Strictly behavior-preserving, test-first, one slice per PR | ▲ (maintainability) | XL | ▲ |
|
||||
| L2 | **Finish the project-layout migration** — physically move linked `Models/`/`Data/`/controller/service files into real projects, retire glob-include `JobTrackerBackend` | ● | L | ● |
|
||||
| L3 | **Vite migration** (CRA/react-scripts is EOL; 4 GB-heap builds) | ● | L | ● |
|
||||
| L4 | **OpenAPI-generated TypeScript client** replacing hand-written `api.ts` surface | ● | M–L | ● |
|
||||
| L5 | **Staging environment / deploy gate** (compose profile or second host; smoke test before prod) | ▲ (ops) | L | ● |
|
||||
|
||||
## Tier 4 — Future Ideas (not scheduled)
|
||||
|
||||
- Full browser extension (Chrome/Firefox store) with autofill.
|
||||
- Web push notifications + weekly digest.
|
||||
- Company research assistant (local AI summarizing company info).
|
||||
- Offer comparison & salary analytics dashboards.
|
||||
- Job feed matching from saved searches (Finn/NAV polling).
|
||||
- Native mobile wrappers; CalDAV/Google Calendar two-way sync.
|
||||
- Multi-instance/scale-out readiness (distributed cache/queue).
|
||||
|
||||
---
|
||||
|
||||
## Recommended execution sequence (Phase 4+)
|
||||
|
||||
Interleaving product and engineering so debt never blocks features:
|
||||
|
||||
1. **Wave 0 (hygiene):** Q3 → Q1 → Q2 → Q4 → Q5 (each a small conventional commit on a feature branch; Q1/Q2 are the two items with real operational risk today)
|
||||
2. **Wave 1 (flagship):** H1 match scoring (design doc → backend endpoint → UI panel → tests)
|
||||
3. **Wave 2 (core UX):** H2 canonical pipeline/kanban, then H3 analytics
|
||||
4. **Wave 3:** H4 Gmail suggestions, M1 bookmarklet, M2 PWA
|
||||
5. **Wave 4:** M3 interview hub, M4 contacts, M5 durable queue
|
||||
6. **Continuous:** L1 controller decomposition proceeds opportunistically — whenever a wave touches a god-controller area, extract that slice first (M6 rides along); L2–L5 scheduled after Wave 3 checkpoint.
|
||||
|
||||
Phases 5–10 of the mission (bug hunt, security audit, performance, refactoring, testing, docs) run after or between waves as checkpoints; Phase 11 rules apply throughout (feature branches, conventional commits, full test suite before commit, no auto-merge to main).
|
||||
|
||||
**Explicitly deprioritized:** auto-apply automation (quality/ToS problems), cloud AI providers (undermines privacy moat), Chrome-store extension before the bookmarklet proves demand.
|
||||
@@ -0,0 +1,122 @@
|
||||
# SECURITY_REPORT.md — Session Change Review
|
||||
|
||||
> Phase 6 deliverable. Scope: security review of the changes made in this work session
|
||||
> (Wave 0 + roadmap H1–H4), plus confirmation that the tenant-isolation model still holds.
|
||||
> Date: 2026-07-03. Complements the prior standalone assessments in
|
||||
> `docs/security-assessments/` (M013 adversarial, M014 remediation, M015 authorization replay).
|
||||
|
||||
This is **not** a full re-audit of the whole application — those live in `docs/security-assessments/`.
|
||||
It is a focused review of the new/changed surface so nothing shipped this session introduces a regression.
|
||||
|
||||
---
|
||||
|
||||
## 1. Summary
|
||||
|
||||
No new vulnerabilities were introduced. Tenant isolation on the new endpoints is carried by the
|
||||
existing `JobTrackerContext` global query filters and is now covered by regression tests. One latent
|
||||
correctness issue (a routable background-service method) was closed, and leaked runtime secrets were
|
||||
removed from version control (rotation recommended — see §6).
|
||||
|
||||
| Severity | Count | Items |
|
||||
|---|---|---|
|
||||
| Critical | 0 | — |
|
||||
| High | 0 | — |
|
||||
| Medium | 1 (mitigated) | DataProtection keys present in git history (untracked this session; rotation recommended) |
|
||||
| Low / hardening | 3 | see §5 |
|
||||
|
||||
---
|
||||
|
||||
## 2. New/changed attack surface reviewed
|
||||
|
||||
| Change | Surface | Verdict |
|
||||
|---|---|---|
|
||||
| `GET /jobapplications/{id}/match-score` | route int id; reads own CV + job | Safe — tenant-scoped |
|
||||
| `GET /jobapplications/{id}/status-suggestion` | route int id; reads own correspondence | Safe — tenant-scoped |
|
||||
| `GET /jobapplications/pipeline` | none (static metadata) | Safe |
|
||||
| `PATCH .../status`, Create/Update (status normalization) | user string → `JobPipeline.Normalize` | Safe — no injection, values stored parameterized |
|
||||
| Structured salary fields | numeric + short strings, `NormalizeSalary` | Safe — clamps negatives, whitelists period |
|
||||
| Automated DB backup (`VACUUM INTO`) | server-controlled path | Safe — see §4 |
|
||||
| Dev OpenAPI (`/openapi/v1.json`) | schema | Safe — `Development` environment only |
|
||||
| `EmailStatusClassifier` | reads stored correspondence text | Safe — deterministic, no eval/injection |
|
||||
|
||||
---
|
||||
|
||||
## 3. OWASP-oriented checklist for the new code
|
||||
|
||||
- **A01 Broken Access Control** — The two new data endpoints load the job via
|
||||
`_db.JobApplications.FirstOrDefaultAsync(j => j.Id == id)`, which is filtered by the global
|
||||
query filter `CurrentUserId != null && OwnerUserId == CurrentUserId` (deny-on-null, hardened in
|
||||
M013-2). A cross-user id returns `NotFound`, not another tenant's data. The correspondence lookup
|
||||
in `status-suggestion` and the `JobEvent` lookup in analytics are likewise filtered through their
|
||||
parent's owner. **Verified by `JobApplicationsAuthorizationTests` (match-score + status-suggestion).**
|
||||
- **A03 Injection** — All new persistence goes through EF Core parameterized queries. The only raw
|
||||
SQL added is `VACUUM INTO '<path>'` with a fully server-derived path (see §4). No string
|
||||
concatenation of user input into queries.
|
||||
- **A03 ReDoS** — New regexes (`JobCvMatchService.TokenPattern`, the revised `SkillTagger` C#/.NET
|
||||
patterns with fixed-width look-behinds) are linear with no catastrophic backtracking.
|
||||
- **A04 Insecure Design** — Status suggestions and match scoring are deterministic and
|
||||
**human-confirmed** (a status only changes when the user clicks). No automated outbound actions.
|
||||
- **A05 Security Misconfiguration** — OpenAPI is exposed only under `IsDevelopment()`; production
|
||||
deployments (`ASPNETCORE_ENVIRONMENT=Production`) do not serve it.
|
||||
- **A08 Data Integrity** — `JobPipeline.Normalize` canonicalizes status on write but preserves
|
||||
unknown custom values (no silent data loss).
|
||||
- **A09 Logging** — No secrets or PII added to logs by the new code.
|
||||
|
||||
---
|
||||
|
||||
## 4. Database backup — path handling
|
||||
|
||||
`SqliteDatabaseBackupRunner` runs `VACUUM INTO '<target>'`. The target is
|
||||
`<Data:Root>/backups/jobtracker_backup_<UTC-timestamp>.db` — no user input reaches it — and single
|
||||
quotes are escaped defensively. Backups contain the full database (sensitive) and are written to the
|
||||
same data volume as the live DB, i.e. the same trust boundary; they are git-ignored. For defense in
|
||||
depth, operators should ship backups off-host with transport encryption and restrict volume
|
||||
permissions. **Recommendation (low):** document an off-host, encrypted backup rotation in the
|
||||
deployment guide.
|
||||
|
||||
---
|
||||
|
||||
## 5. Low / hardening findings
|
||||
|
||||
1. **match-score input size (low).** `GetMatchScore` does not cap job-description length before
|
||||
tokenizing. Descriptions are bounded in practice (imported/typed), and the algorithm is linear, so
|
||||
this is not a DoS, but a defensive cap (e.g. 50 KB) would be prudent.
|
||||
2. **New read endpoints are not rate-limited (low).** `match-score`/`status-suggestion` are cheap and
|
||||
deterministic (no AI, one indexed query), and auth-gated in production, so abuse potential is low.
|
||||
Consider a general authenticated-read limiter if the API is exposed publicly.
|
||||
3. **status-suggestion is conservative for custom statuses (informational).** A job in a non-canonical
|
||||
custom status (pipeline order = max) never receives a suggestion. This is safe (fails closed) but
|
||||
slightly under-surfaces; acceptable given custom statuses are rare.
|
||||
|
||||
---
|
||||
|
||||
## 6. Secrets hygiene (actioned this session)
|
||||
|
||||
- Committed ASP.NET **DataProtection key XML files** (`keys/`, `JobTrackerApi/keys/`) and daily export
|
||||
JSON were removed from tracking and added to `.gitignore`
|
||||
(commit `security: untrack DataProtection keys and runtime exports…`).
|
||||
- **These key files remain in git history.** DataProtection keys sign auth/session artifacts, so
|
||||
**rotating them on the production host is recommended** (generate fresh keys; the app regenerates the
|
||||
key ring in the persisted `keys/` directory on next start). Until rotated, anyone with history access
|
||||
could read the old key material.
|
||||
- Local `.env` remains git-ignored; `appsettings.Development.json` contains only `CHANGE_ME_*`
|
||||
placeholders. No live secrets are tracked.
|
||||
|
||||
---
|
||||
|
||||
## 7. Confirmed intact from prior assessments
|
||||
|
||||
Spot-checked that the M013–M015 remediations are still in force after this session's changes:
|
||||
|
||||
- Owner query filters still deny on null `CurrentUserId` (`Data/JobTrackerContext.cs`).
|
||||
- Local JWT still requires a concrete subject claim (`LocalAuthIdentity`, `Program.cs`).
|
||||
- Job-import SSRF guard (DNS resolution + private-range rejection, redirects disabled) untouched.
|
||||
- CSRF double-submit middleware and CORS allowlist untouched.
|
||||
|
||||
---
|
||||
|
||||
## 8. Retest
|
||||
|
||||
All backend tests pass (135), including the two new tenant-isolation tests for the new endpoints.
|
||||
No fix in this report required code changes beyond what already landed; the residual **action for the
|
||||
operator is DataProtection key rotation** (§6).
|
||||
@@ -0,0 +1,293 @@
|
||||
# Jobbjakt (Job Tracker) — System Overview
|
||||
|
||||
> Phase 1 deliverable: full-system map produced before any code changes.
|
||||
> Last updated: 2026-07-02. Verified against commit `eea327e1` plus local working-tree changes.
|
||||
|
||||
---
|
||||
|
||||
## 1. What the product is
|
||||
|
||||
Jobbjakt is a self-hosted, multi-user job application tracking platform with heavy AI assistance:
|
||||
|
||||
- Track job applications end-to-end (status pipeline, follow-ups, deadlines, salary, tags, notes).
|
||||
- Company/recruiter CRM (pipeline stage, contact dates, recruiter details).
|
||||
- Correspondence log per application, including **Gmail OAuth import with review workflow**.
|
||||
- Attachments per application with purpose metadata and AI-inclusion toggles.
|
||||
- **CV platform**: upload → OCR/text extraction → structured CV parsing (Ollama-assisted block classification) → per-job tailored CV drafts → templated PDF export via Playwright.
|
||||
- AI drafts: cover letters, recruiter messages, follow-up drafts, job description summaries, translation (LibreTranslate optional).
|
||||
- Rules engine (auto-ghosting, follow-up "needs attention"), reminder emails, daily JSON export, history/event trail, encrypted backup (Windows/DPAPI).
|
||||
- Admin surface: user management, audit log, system readiness page.
|
||||
- Deployed to production at `https://jobs.cesnimda.uk` via Gitea Actions → SSH → Docker Compose.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture overview
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Client
|
||||
UI[React 19 SPA<br/>MUI 7, react-router 6<br/>CRA/react-scripts]
|
||||
end
|
||||
|
||||
subgraph Frontend container
|
||||
NGINX[nginx 1.29-alpine<br/>serves build + proxies /api]
|
||||
end
|
||||
|
||||
subgraph Backend container
|
||||
API[ASP.NET Core net9.0 API<br/>JobTrackerApi host]
|
||||
BG[Hosted services:<br/>Rules, FollowUpReminder,<br/>DailyExport, JobEnrichment,<br/>SummarizerProbe, CvProcessing]
|
||||
DB[(SQLite default<br/>or MariaDB/MySQL)]
|
||||
FS[/Data root:<br/>Attachments, CvArtifacts,<br/>exports, DP keys/]
|
||||
end
|
||||
|
||||
subgraph AI stack
|
||||
AISVC[FastAPI ai-service :8001<br/>distilbart summarizer,<br/>OCR pytesseract/PyMuPDF,<br/>docx/pdf extraction]
|
||||
OLLAMA[Ollama :11434<br/>qwen2.5:7b<br/>CV classification + rewrite]
|
||||
end
|
||||
|
||||
EXT1[Google OAuth / Gmail API]
|
||||
EXT2[Job sites: Finn, NAV,<br/>LinkedIn, Jobbnorge]
|
||||
EXT3[SMTP - Gmail app password]
|
||||
EXT4[LibreTranslate optional]
|
||||
|
||||
UI --> NGINX --> API
|
||||
API --> DB
|
||||
API --> FS
|
||||
API --> AISVC --> OLLAMA
|
||||
API --> EXT1
|
||||
API --> EXT2
|
||||
API --> EXT3
|
||||
API --> EXT4
|
||||
BG --> DB
|
||||
```
|
||||
|
||||
### Solution layout (unusual — read this first)
|
||||
|
||||
| Project | Role |
|
||||
|---|---|
|
||||
| `JobTrackerApi/` | Web **host** only: `Program.cs`, appsettings, migrations, Dockerfile. Its csproj **excludes** `Controllers/**` and `Services/**` from its own compilation. |
|
||||
| `JobTrackerBackend/` | "Transitional shared-backend" **library** that compiles, via `<Compile Include>` links, the files physically located in `../Data`, `../Models`, `../JobTrackerApi/Controllers`, `../JobTrackerApi/Services`. Exists so tests can reference controllers/services without the web-entry project. |
|
||||
| `JobTrackerApi.Tests/` | xUnit test project (~20 test classes incl. authorization/hostile-fixture tests). |
|
||||
| `Models/`, `Data/` (repo root) | The *real* EF models and `JobTrackerContext`, compiled into JobTrackerBackend. |
|
||||
| `Controller/` (repo root) | **Legacy stub controllers (~1 KB each) — dead code**, not referenced by any csproj. |
|
||||
| `job-tracker-ui/` | React SPA. |
|
||||
| `tools/summarizer/` | FastAPI AI service (own Dockerfile, pytest tests). |
|
||||
| `deploy/`, `.gitea/workflows/` | Prod deploy script + CI/CD pipeline. |
|
||||
| `docs/` | Session handoffs, security assessments (M013–M015), UAT notes. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Technology stack
|
||||
|
||||
**Backend**: ASP.NET Core net9.0, EF Core 9 (SQLite default; Pomelo MySQL/MariaDB switchable via `Database:Provider`), ASP.NET Identity Core (users/roles), JWT bearer auth (local + Google policy scheme), built-in RateLimiter, DataProtection (file-system keys), Playwright (CV PDF export).
|
||||
|
||||
**Frontend**: React 19, TypeScript 4.9, MUI 7 (+ x-data-grid, x-date-pickers, lab), axios, react-router-dom 6, @tanstack/react-table, CRA `react-scripts` 5 (build needs `--max-old-space-size=4096`), i18n EN + NB (custom provider), Jest/RTL tests.
|
||||
|
||||
**AI**: FastAPI + transformers (`sshleifer/distilbart-cnn-12-6`) for summaries; pytesseract/PyMuPDF/pypdf/python-docx for extraction/OCR; Ollama (`qwen2.5:7b`) for CV block classification and rewrite paths; TTL cache.
|
||||
|
||||
**Infra**: Docker Compose (4 services: backend, frontend/nginx, ai-service, ollama w/ GPU), Gitea Actions CI (build + backend tests + selected frontend tests + frontend build) → SSH deploy → `deploy/deploy.sh` on the prod host, external `jobtracker_shared` network.
|
||||
|
||||
---
|
||||
|
||||
## 4. Authentication & authorization
|
||||
|
||||
- **Smart policy scheme**: inspects the bearer token issuer — Google-issued ID tokens (`accounts.google.com`) route to the `google` JWT handler (validated against `Auth:GoogleClientId`); everything else routes to `local` JWT (symmetric key `Auth:JwtKey`, issuer/audience validated, 2-min clock skew).
|
||||
- **Cookie session support**: local handler also reads the session cookie (`AuthSessionOptions.SessionCookieName`); **CSRF double-submit** middleware enforces cookie+header match for all mutating requests when a session cookie is present (login/register/reset/csrf endpoints exempt).
|
||||
- `Auth:Require=true` sets a fallback authorize-all policy (prod compose sets it). Dev without a JWT key generates an ephemeral key + warning; **fails closed** if auth required but no key.
|
||||
- Local tokens **must** carry a subject claim (`LocalAuthIdentity`), enforced in `OnTokenValidated` — hardened after finding M013-2.
|
||||
- **Multi-tenancy**: every tenant entity carries `OwnerUserId`; `JobTrackerContext` applies global query filters `CurrentUserId != null && OwnerUserId == CurrentUserId` (deny-on-null). Correspondence/JobEvents/CV entities filter through their parent's owner.
|
||||
- Roles via ASP.NET Identity: admin-only controllers (`UsersController`, `AdminAuditController`, `AdminSystemController`).
|
||||
- Password policy: min 8, digit + lowercase required. Password reset via emailed token (SMTP required). Registration disabled by default.
|
||||
- Rate limiting: `auth-login` (10/5 min/IP) and `auth-email` (5/15 min/IP) fixed-window policies.
|
||||
|
||||
---
|
||||
|
||||
## 5. Database schema (EF Core, 8 migrations)
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
ApplicationUser ||--o{ Company : owns
|
||||
ApplicationUser ||--o{ JobApplication : owns
|
||||
ApplicationUser ||--o| UserRuleSettings : has
|
||||
ApplicationUser ||--o{ GmailConnection : has
|
||||
ApplicationUser ||--o{ CvUploadArtifact : owns
|
||||
ApplicationUser ||--o{ CvExtractionRun : owns
|
||||
Company ||--o{ JobApplication : "has jobs"
|
||||
JobApplication ||--o{ Correspondence : messages
|
||||
JobApplication ||--o{ Attachment : attachments
|
||||
JobApplication ||--o{ JobEvent : events
|
||||
JobApplication ||--o| TailoredCvDraft : "1:1 draft"
|
||||
CvUploadArtifact ||--o{ CvExtractionRun : "source of"
|
||||
ApplicationUser ||--o{ GmailReviewDecision : decides
|
||||
```
|
||||
|
||||
Key notes:
|
||||
|
||||
- `ApplicationUser` (IdentityUser) also stores profile CV text, **structured CV JSON** (`ProfileCvStructureJson`), avatar data-URL, Google link info, current CV artifact/run pointers.
|
||||
- `JobApplication`: status string (default "Applied"), soft delete (`IsDeleted`/`DeletedAt`), tags as JSON string, imported description + translation, persisted `ShortSummary`, tailored CV text, reminder bookkeeping. Cascade deletes to messages/attachments/events/draft.
|
||||
- `RuleSettings` (global, seeded Id=1) + per-user `UserRuleSettings`.
|
||||
- `SystemEmailSettings`: DB-stored SMTP override (resolved by `EmailSettingsResolver`).
|
||||
- Indexes: `OwnerUserId` on Company/JobApplication/GmailConnection; composite `(OwnerUserId, UploadedAtUtc)`, `(OwnerUserId, StartedAtUtc)`, unique `(OwnerUserId, JobApplicationId)` on draft, unique `(OwnerUserId, GmailAddress)`.
|
||||
- SQLite file lives at `DataRoot/jobtracker.db` (WAL mode); migrations applied automatically at startup (`StartupInitializationExtensions`, 62 KB — also seeds admin, creates Identity tables where `dotnet ef` unavailable, ignores `PendingModelChangesWarning`).
|
||||
|
||||
---
|
||||
|
||||
## 6. API surface (all under `/api`, ~15 controllers)
|
||||
|
||||
| Controller | Highlights |
|
||||
|---|---|
|
||||
| `JobApplicationsController` (**151 KB!**) | CRUD, paging/filtering/sorting, board, reminders, stats, history, unified timeline, status/follow-up PATCH, soft delete/restore, **plus** AI surface: application package material, follow-up drafts, cover-letter/recruiter drafts ("Maria" drafts), workflow signals. |
|
||||
| `ProfileCvController` (**117 KB**) | CV upload artifacts, extraction runs, structure parsing, rebuild/improve, tailored CV generation via Ollama rewrite, template rendering + Playwright PDF preview/export, benchmark corpus harness. |
|
||||
| `GmailController` (**60 KB**) | OAuth connect/callback, sync, message review queue, import decisions, job matching. |
|
||||
| `AuthController` (22 KB) | login/register/me/config, Google exchange, password reset request/reset, session cookie + CSRF endpoints. |
|
||||
| `CompaniesController` | CRUD, idempotent create by name, recruiter/pipeline fields. |
|
||||
| `CorrespondenceController` | per-job messages CRUD. |
|
||||
| `AttachmentsController` | multipart upload to disk, download, rename, delete, purpose/AI-inclusion metadata. |
|
||||
| `RulesController` | global + per-user rule settings, clamped. |
|
||||
| `ExportController` | JSON/CSV export. |
|
||||
| `BackupController` | DPAPI-encrypted backup (Windows only). |
|
||||
| `JobImportController` | URL preview via plugin parsers (SSRF-hardened). |
|
||||
| `UsersController`, `AdminAuditController`, `AdminSystemController` | admin: user/role management, audit trail, system readiness (DB/Gmail/AI). |
|
||||
| `ClientErrorsController` | frontend error intake → logs. |
|
||||
|
||||
No OpenAPI/Swagger is wired up; the README is the de-facto API doc (already drifting).
|
||||
|
||||
---
|
||||
|
||||
## 7. Background services (6 hosted services)
|
||||
|
||||
| Service | Function |
|
||||
|---|---|
|
||||
| `RulesHostedService` → `RulesEngine` | periodic auto-transitions (e.g., → Ghosted) from rule settings |
|
||||
| `FollowUpReminderHostedService` | reminder emails for due/upcoming follow-ups (dedup via `LastReminderEmailSentAt`) |
|
||||
| `DailyExportHostedService` | daily JSON export at configured local hour |
|
||||
| `JobEnrichmentHostedService` | backfills summaries/enrichment for jobs |
|
||||
| `SummarizerProbeHostedService` | probes AI service readiness |
|
||||
| `CvProcessingHostedService` + `CvProcessingQueue` | in-memory queue for CV extraction/processing jobs |
|
||||
|
||||
All state is in-process (`IMemoryCache`, in-memory queue) — single-instance assumption; no distributed locks; queue contents lost on restart.
|
||||
|
||||
---
|
||||
|
||||
## 8. AI pipeline (data flow)
|
||||
|
||||
1. **Job import**: URL → plugin parse (Finn/NAV/LinkedIn/Jobbnorge or universal JSON-LD parser) → optional LibreTranslate → language detect + skill tagging → preview → user accepts → stored on `JobApplication`.
|
||||
2. **Summaries**: API → `SummarizerService` (31 KB) → FastAPI `/summarize` (distilbart, TTL-cached, GPU-if-available) → persisted `ShortSummary`.
|
||||
3. **CV ingest**: upload (PDF/DOCX/image ≤ 8 MB) → FastAPI extract/OCR → block classification (Ollama-assisted, `CvAiClassifier`/`CvAiNormalizer`) → `ProfileCvStructureJson` on user.
|
||||
4. **Tailoring**: job description + structured CV sections → Ollama rewrite path (recent commits: clamped lengths, hardened diagnostics) → `TailoredCvDraft` (JSON blocks) → `CvTemplateRenderer` (25 KB, template carousel) → Playwright → PDF.
|
||||
5. **Drafts**: cover letter / recruiter message / follow-up drafts generated per job with attachment-aware context selection.
|
||||
|
||||
Degradation: if AI service or Ollama is down, core tracking still works (probe service + "AI is not a deploy gate" in CI).
|
||||
|
||||
---
|
||||
|
||||
## 9. Email
|
||||
|
||||
- `SmtpEmailSender` with `EmailSettingsResolver`: config from env/appsettings **or** DB-stored `SystemEmailSettings` (admin-editable).
|
||||
- Uses Gmail SMTP + app password in prod. Flows: password reset, follow-up reminders. `App:PublicBaseUrl` builds links.
|
||||
|
||||
---
|
||||
|
||||
## 10. Configuration & secrets
|
||||
|
||||
- `.env` (git-ignored) → docker-compose env → ASP.NET config. `.env.example` documents the shape. Real secrets currently present in local `.env` (JWT key, admin password, SMTP app password, Google client secret).
|
||||
- `appsettings.Development.json` contains only `CHANGE_ME_*` placeholders (good).
|
||||
- Key knobs: `Database:Provider`, `ConnectionStrings:JobTracker`, `Data:Root`, `Cors:Origins`, `Ai:BaseUrl`, `Auth:*`, `Email:*`, `Exports:*`, `Translation:*`, `App:PublicBaseUrl`, `HttpsRedirection:*` (TLS terminated at reverse proxy; HSTS/redirect off in-container).
|
||||
- `ProductionConfigTests.cs` exists to guard prod config shape.
|
||||
|
||||
---
|
||||
|
||||
## 11. Build, CI/CD, deployment
|
||||
|
||||
- **CI** (`.gitea/workflows/ci-deploy.yml`): on PR + push-to-main → build backend (Release), run backend tests, `npm ci`, run an **explicit whitelist of 10 frontend test files** (not the whole suite), build frontend.
|
||||
- **Deploy** (push to main only): SSH to prod host → `git reset --hard <sha>` in `/opt/job-tracker/app` → `deploy/deploy.sh` (docker compose build/up with retry/cache-prune fallbacks) → verify containers; AI service health is non-blocking.
|
||||
- Frontend Dockerfile: node build stage → nginx 1.29-alpine (working-tree bump from 1.27 pending commit); nginx proxies `/api` to backend.
|
||||
- No staging environment; deploys go straight to prod after CI.
|
||||
|
||||
---
|
||||
|
||||
## 12. Testing strategy
|
||||
|
||||
- **Backend**: xUnit integration-style tests via `TestHostFactory`; notable coverage: authorization (`JobApplicationsAuthorizationTests`, `OwnershipGuardTests`, hostile fixture DB project), auth/system, Gmail, CV corpus harness, summarizer, SQLite migration helper, production config.
|
||||
- **Frontend**: ~20 Jest/RTL test files (workspace flows, Gmail review, login, admin, attachments, drafts, trust-loop e2e-ish component tests). CI runs only the whitelisted subset.
|
||||
- **AI service**: pytest (`tools/summarizer/tests/test_app.py`).
|
||||
- No true end-to-end browser tests; no load/perf tests.
|
||||
|
||||
---
|
||||
|
||||
## 13. Logging & error handling
|
||||
|
||||
- Console/debug logging; custom middleware logs every request (method, path, status, ms, traceId, sub claim). Unhandled exceptions logged then rethrown (500).
|
||||
- Client errors POSTed to `/api/client-errors` and logged server-side; React `ErrorBoundary` + route error page in UI.
|
||||
- No structured sink (Seq/OTLP), no log rotation policy in-app (container stdout), no correlation to frontend errorIds beyond log text, no ProblemDetails standardization.
|
||||
|
||||
---
|
||||
|
||||
## 14. Security posture (current)
|
||||
|
||||
Strong points (much already hardened via M013–M015 adversarial assessments in `docs/security-assessments/`):
|
||||
|
||||
- SSRF on job import **fixed & retested** (DNS resolution check, private/loopback/link-local rejection, redirects disabled).
|
||||
- Subjectless-JWT / owner-filter bypass **fixed & retested** (fail-closed identity, deny-on-null query filters).
|
||||
- Cross-user job history leak fixed (`81196374`); authorization replay findings recorded (M015).
|
||||
- CSRF double-submit for cookie sessions; CORS allowlist; rate-limited login/email endpoints; ephemeral JWT key refused when auth required; Identity password hashing (PBKDF2); DataProtection keys persisted outside repo runtime path.
|
||||
|
||||
Open questions / watch areas (to verify in Phase 6):
|
||||
|
||||
- `AllowCredentials()` combined with configurable `Cors:Origins="*"` wildcard mode (SetIsOriginAllowed(true) + credentials) — dangerous if ever enabled.
|
||||
- Attachment upload: file-type/size limits, path handling, content-type on download need re-audit.
|
||||
- Avatar stored as data-URL on user record (size/XSS considerations).
|
||||
- Gmail OAuth token storage encryption at rest; scopes; audit of `GmailController` (60 KB).
|
||||
- Global rate limiting only on 2 auth policies — AI/expensive endpoints unthrottled.
|
||||
- Backup endpoint Windows-only DPAPI — silently unavailable on Linux prod.
|
||||
- Dependency freshness (axios, react-scripts 5/CRA is deprecated upstream; transformers/torch pinning).
|
||||
- Secrets present in local `.env` (expected, git-ignored) — confirm no history leaks.
|
||||
|
||||
---
|
||||
|
||||
## 15. Technical debt report
|
||||
|
||||
1. **God controllers**: `JobApplicationsController` (151 KB), `ProfileCvController` (117 KB), `GmailController` (60 KB), `StartupInitializationExtensions` (62 KB). Massive single files mixing HTTP, business logic, AI prompt construction, and persistence. Highest-leverage refactor target — but high risk, needs test cover first.
|
||||
2. **Transitional project layout**: `JobTrackerBackend` compiles files it doesn't own via glob includes; root `Models/`/`Data/` folders; **dead** root `Controller/` folder; `JobTrackerBackend/bin`+`obj` artifacts and `JobTrackerApi/jobtracker.db` + `bin_build/`, `CvArtifacts/`, `exports/`, `keys/` polluting the repo/working tree. `.gitignore` needs review.
|
||||
3. **CI runs a hand-maintained subset** of frontend tests — new test files silently not run (already bit them once; `profile-page.test.tsx` had to be added manually).
|
||||
4. **CRA/react-scripts 5** is EOL-ish, slow builds (needs 4 GB heap), TS 4.9. Vite migration is the obvious path (medium effort).
|
||||
5. **Naming drift**: `Summarizer*` vs `AiService*`; "Jobbjakt" vs "Job Tracker" branding split; EN/NB translation consistency flagged in handoff doc.
|
||||
6. No OpenAPI; README endpoint list already drifts from code (e.g., Gmail/profile/admin endpoints missing there).
|
||||
7. In-memory queue/cache single-instance coupling undocumented.
|
||||
8. Root-level clutter: `temp_job.json`, `temp_post_job.py`, `todo jobtracker.txt`, `test/`, `tmp/`, `vendor/`, `.venv/`.
|
||||
9. `DaysSince` compares `DateTime.UtcNow` with `.Days` truncation — timezone/UX edge cases; status is a free string, no canonical pipeline enum (README itself lists this as a wanted improvement).
|
||||
10. Windows-only backup path.
|
||||
|
||||
---
|
||||
|
||||
## 16. Areas of concern
|
||||
|
||||
- **Single point of data**: SQLite in a Docker volume; backups are manual/Windows-only; no automated off-host backup.
|
||||
- **Deploy risk**: `git reset --hard` + straight-to-prod with no staging and non-exhaustive CI test coverage.
|
||||
- **AI coupling**: prompt logic buried in controllers makes model/provider changes and testing hard.
|
||||
- **Restart data loss**: queued CV processing jobs are lost on restart (in-memory queue).
|
||||
- **Uncommitted working tree**: 3 modified files (Dockerfile nginx bump, `useViewResource` stale-closure fix, handoff doc) + untracked `scripts/start-ollama-cv.ps1` and a stray `JobTrackerApi/CvArtifacts/` data folder.
|
||||
|
||||
---
|
||||
|
||||
## 17. Opportunities for improvement (input to Phase 2/3)
|
||||
|
||||
Product (initial hypotheses, to be validated by market research):
|
||||
|
||||
- Canonical pipeline model + customizable Kanban stages (already on README wish list).
|
||||
- Interview scheduling/prep hub (calendar integration, prep notes, question banks).
|
||||
- Salary/offer comparison and analytics dashboards (funnel conversion, response rates, time-in-stage).
|
||||
- Browser extension / bookmarklet for one-click job capture (plugins already exist server-side).
|
||||
- Saved searches/views, full-text search, date-range and tag filters.
|
||||
- Notifications beyond email (web push, digest).
|
||||
- Contact-level recruiter CRM (people, not just companies).
|
||||
- Mobile-friendly PWA pass.
|
||||
|
||||
Engineering:
|
||||
|
||||
- Swagger/OpenAPI + generated TS client; ProblemDetails everywhere.
|
||||
- Split god controllers into feature services; move AI prompting behind interfaces.
|
||||
- Run full frontend test suite in CI (`npm test -- --watchAll=false` without whitelist) once flaky tests are addressed; add `dotnet format`/eslint gates.
|
||||
- Vite migration; dependency refresh.
|
||||
- Durable job queue (DB-backed) for CV processing; automated DB backup job.
|
||||
- Repo hygiene: delete dead `Controller/`, ignore build artifacts, remove committed DB files.
|
||||
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJKb2JUcmFja2VyQXBpIiwiYXVkIjoiam9iLXRyYWNrZXItdWkiLCJuYmYiOjE3NzQ2MDE0ODEsImV4cCI6MTc3NDY0NDY4NiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiIyM2RjMTk2Yi1mMjI3LTQ0OTktOTNmZS00MDNkODgwMWUyMWMiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJhZG1pbkBleGFtcGxlLmNvbSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJhZG1pbkBleGFtcGxlLmNvbSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluIn0.xraA-RMiGjTYEwH27uwgoDrqkgTkVa86Q54A9b15Vts
|
||||
@@ -0,0 +1,20 @@
|
||||
# Jobjakt Development Context
|
||||
|
||||
Before making changes:
|
||||
|
||||
1. Read docs/00-ai-context.md
|
||||
2. Read relevant feature documentation
|
||||
3. Check existing implementation
|
||||
4. Prefer incremental changes
|
||||
|
||||
The application is a job tracking platform.
|
||||
|
||||
The CV builder is a supporting Career Workspace feature.
|
||||
|
||||
Never redesign Jobjakt into only a CV builder.
|
||||
|
||||
Existing working functionality must be preserved.
|
||||
|
||||
When uncertain:
|
||||
- Ask for clarification.
|
||||
- Do not make large assumptions.
|
||||
@@ -0,0 +1,224 @@
|
||||
# CV builder, parser, benchmark, and Ollama admin integration
|
||||
|
||||
## What changed
|
||||
|
||||
This branch upgrades the Profile CV flow from a text-only rewrite helper into a template-driven CV builder backed by the server-side renderer/PDF pipeline, strengthens CV normalization around location and qualification handling, adds a repeatable local corpus benchmark workflow, and expands the admin system page with richer Ollama visibility.
|
||||
|
||||
## Profile CV builder
|
||||
|
||||
### New backend capabilities
|
||||
|
||||
`JobTrackerApi/Controllers/ProfileCvController.cs`
|
||||
|
||||
- Hardened `POST /api/profile-cv/rewrite-section`
|
||||
- accepts flexible `jobApplicationId` payloads (number or blank string)
|
||||
- uses richer saved-job context for tailoring
|
||||
- logs empty AI responses with useful context
|
||||
- Added `GET /api/profile-cv/templates`
|
||||
- Added `POST /api/profile-cv/rewrite-preview`
|
||||
- rewrites either the whole CV or one selected section
|
||||
- rebuilds structured CV from the rewritten full text
|
||||
- maps the result into the shared template renderer
|
||||
- returns rendered HTML, file name, rewritten text, and full replacement text
|
||||
- Added `POST /api/profile-cv/export-pdf`
|
||||
- uses the same rendered HTML and the shared Playwright exporter
|
||||
|
||||
### Frontend flow
|
||||
|
||||
`job-tracker-ui/src/pages/ProfilePage.tsx`
|
||||
|
||||
- Replaced the old rewrite draft box with a template-driven builder section.
|
||||
- Users can:
|
||||
- choose from 6 templates
|
||||
- optionally target one section
|
||||
- target by free-text role or saved job
|
||||
- inspect the rewritten content
|
||||
- inspect the actual rendered preview
|
||||
- download a PDF
|
||||
- replace the master CV with the rebuilt full-text result
|
||||
|
||||
## Templates
|
||||
|
||||
Shared renderer: `JobTrackerApi/Services/CvTemplateRenderer.cs`
|
||||
|
||||
Available templates:
|
||||
- `ats-minimal`
|
||||
- `harvard`
|
||||
- `auckland`
|
||||
- `edinburgh`
|
||||
- `monarch`
|
||||
- `fjord`
|
||||
|
||||
### Adding a new template
|
||||
|
||||
1. Add the new template id to `NormalizeTemplateId()` in:
|
||||
- `JobTrackerApi/Services/CvTemplateRenderer.cs`
|
||||
- `JobTrackerApi/Controllers/ProfileCvController.cs`
|
||||
2. Add a render branch in `CvTemplateRenderer.Render()`.
|
||||
3. Add a descriptor to `GetCvTemplateDescriptors()`.
|
||||
4. Add the matching card entry in `job-tracker-ui/src/pages/ProfilePage.tsx` if you want a custom preview card.
|
||||
|
||||
## PDF generation
|
||||
|
||||
The master CV builder now reuses the existing server-side pipeline:
|
||||
|
||||
1. rewrite full text / section
|
||||
2. rebuild structured CV
|
||||
3. map to `TailoredCvDocument`
|
||||
4. render HTML via `ICvTemplateRenderer`
|
||||
5. export PDF via `ICvPdfExporter` / Playwright
|
||||
|
||||
This keeps PDF output visually aligned with the selected template and avoids a separate client-only print implementation.
|
||||
|
||||
## Parser and structured CV changes
|
||||
|
||||
### Shared schema
|
||||
|
||||
`Models/StructuredCvProfile.cs`
|
||||
|
||||
Added:
|
||||
- `education[].qualificationLevel`
|
||||
- top-level `certifications[]`
|
||||
- top-level `projects[]`
|
||||
|
||||
`qualification` remains the original preserved text.
|
||||
|
||||
### Normalization improvements
|
||||
|
||||
`Models/StructuredCvProfileJson.cs`
|
||||
|
||||
- tighter location sanitization to avoid skill or role spillover into location fields
|
||||
- qualification level normalization to one of:
|
||||
- `Secondary`
|
||||
- `Diploma/Certificate`
|
||||
- `Bachelor`
|
||||
- `Master`
|
||||
- `PhD`
|
||||
- `Other`
|
||||
- first-class normalization for certifications and projects
|
||||
- section reconstruction now includes certifications and projects
|
||||
|
||||
### Extraction prompt improvements
|
||||
|
||||
`JobTrackerApi/Controllers/ProfileCvController.cs`
|
||||
|
||||
The LLM extraction prompt now explicitly asks for:
|
||||
- qualification level enum
|
||||
- certifications
|
||||
- projects
|
||||
- strict location separation rules
|
||||
- preservation of original qualification text
|
||||
|
||||
## Benchmark workflow
|
||||
|
||||
### Runner
|
||||
|
||||
Use:
|
||||
|
||||
```bash
|
||||
./scripts/run-cv-benchmark.sh
|
||||
```
|
||||
|
||||
Optional overrides:
|
||||
|
||||
```bash
|
||||
CV_BENCHMARK_OUTPUT_DIR=/absolute/output/path \
|
||||
CV_BENCHMARK_APPROVED_DIR=/absolute/approved/fixtures/path \
|
||||
./scripts/run-cv-benchmark.sh
|
||||
```
|
||||
|
||||
### Inputs
|
||||
|
||||
The runner scans:
|
||||
|
||||
- `/home/pi/cvs`
|
||||
|
||||
Supported corpus file types:
|
||||
- PDF
|
||||
- DOCX
|
||||
- TXT
|
||||
- MD
|
||||
|
||||
### Outputs
|
||||
|
||||
The runner writes:
|
||||
|
||||
- `index.json` — machine-readable summary
|
||||
- `report.md` — markdown overview
|
||||
- `outputs/*.json` — latest normalized structured output per CV
|
||||
- `candidate-fixtures/*.json` — created when no approved fixture exists yet
|
||||
|
||||
Approved fixtures are local by design and should be reviewed manually before being promoted into the approved fixture path you use for regression comparisons.
|
||||
|
||||
### Admin review
|
||||
|
||||
`GET /api/admin/system/cv-benchmark`
|
||||
|
||||
The admin system page surfaces:
|
||||
- benchmark root path
|
||||
- last benchmark update time
|
||||
- latest markdown summary
|
||||
|
||||
## Ollama admin visibility
|
||||
|
||||
### Python health endpoint
|
||||
|
||||
`tools/summarizer/app.py`
|
||||
|
||||
`GET /health` now returns additional Ollama metadata when configured/reachable:
|
||||
- `ollama_version`
|
||||
- `ollama_installed_models`
|
||||
- `ollama_loaded_models`
|
||||
- `ollama_loaded_count`
|
||||
|
||||
### Backend propagation
|
||||
|
||||
`JobTrackerApi/Services/SummarizerService.cs`
|
||||
|
||||
The backend metrics shape now carries those fields through to admin consumers.
|
||||
|
||||
### Admin UI
|
||||
|
||||
`job-tracker-ui/src/pages/AdminSystemPage.tsx`
|
||||
|
||||
The system page now shows:
|
||||
- Ollama version
|
||||
- loaded model count
|
||||
- installed model chips
|
||||
- loaded model chips
|
||||
- benchmark summary panel
|
||||
|
||||
## Verification used on this branch
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter ProfileCvControllerTests
|
||||
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter "ProfileCvControllerTests|AuthAndSystemControllerTests|JobApplicationsApplicationPackageTests"
|
||||
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter CvCorpusHarnessTests
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
cd job-tracker-ui && CI=true npm test -- --runInBand --watch=false src/profile-page.test.tsx
|
||||
cd job-tracker-ui && CI=true npm test -- --runInBand --watch=false src/admin-system-page.test.tsx
|
||||
cd job-tracker-ui && CI=true npm test -- --runInBand --watch=false src/profile-page.test.tsx src/admin-system-page.test.tsx src/job-details-generated-drafts.test.tsx
|
||||
```
|
||||
|
||||
### Benchmark runner
|
||||
|
||||
```bash
|
||||
CV_BENCHMARK_OUTPUT_DIR="$(pwd)/tmp/cv-benchmarks/latest" \
|
||||
CV_BENCHMARK_APPROVED_DIR="$(pwd)/tmp/cv-benchmarks/approved" \
|
||||
./scripts/run-cv-benchmark.sh
|
||||
```
|
||||
|
||||
### Python service tests
|
||||
|
||||
The summarizer Python unit tests were updated for the new health payload, but this machine currently lacks `pip` / `venv` support (`python3 -m venv` fails because `python3.12-venv` is not installed), so test execution is environment-blocked here. Once Python packaging is available, run:
|
||||
|
||||
```bash
|
||||
cd tools/summarizer
|
||||
python3 -m pytest -q tests/test_app.py
|
||||
```
|
||||
@@ -0,0 +1,732 @@
|
||||
# AI Development Context
|
||||
## Jobjakt
|
||||
|
||||
Version: 1.0
|
||||
Status: Living document
|
||||
Last Updated: YYYY-MM-DD
|
||||
|
||||
---
|
||||
|
||||
# Purpose
|
||||
|
||||
This document is the primary source of truth for AI-assisted development.
|
||||
|
||||
Every AI assistant (Claude Code, Codex, ChatGPT, GitHub Copilot, etc.) should read this document before making architectural decisions or implementing new features.
|
||||
|
||||
If another document conflicts with this file, this file takes precedence unless an Architecture Decision Record (ADR) explicitly supersedes it.
|
||||
|
||||
---
|
||||
|
||||
# What is Jobjakt?
|
||||
|
||||
Jobjakt is an AI-powered job tracking and career management platform.
|
||||
|
||||
Its purpose is to help people organise, improve and manage their entire job search from one place.
|
||||
|
||||
Unlike traditional job trackers, Jobjakt assists users throughout the complete application lifecycle using AI-powered career tools.
|
||||
|
||||
The application is designed to evolve into a SaaS platform but currently prioritises an excellent single-user experience.
|
||||
|
||||
---
|
||||
|
||||
# Product Philosophy
|
||||
|
||||
Jobjakt is NOT:
|
||||
|
||||
- just a CV builder
|
||||
- just another AI wrapper
|
||||
- another job board
|
||||
- another Kanban clone
|
||||
|
||||
Instead, Jobjakt is a Career Workspace.
|
||||
|
||||
The job tracking workflow remains the primary product.
|
||||
|
||||
Everything else exists to improve the user's ability to secure employment.
|
||||
|
||||
Always remember:
|
||||
|
||||
Jobs are the product.
|
||||
|
||||
Career tools support the product.
|
||||
|
||||
Never invert this relationship.
|
||||
|
||||
---
|
||||
|
||||
# Product Hierarchy
|
||||
|
||||
Core Product
|
||||
|
||||
Job Tracking
|
||||
|
||||
↓
|
||||
|
||||
Applications
|
||||
|
||||
↓
|
||||
|
||||
Application Workflow
|
||||
|
||||
↓
|
||||
|
||||
Communication
|
||||
|
||||
↓
|
||||
|
||||
Follow Ups
|
||||
|
||||
↓
|
||||
|
||||
Career Workspace
|
||||
|
||||
↓
|
||||
|
||||
Career Profile
|
||||
|
||||
↓
|
||||
|
||||
CV Builder
|
||||
|
||||
↓
|
||||
|
||||
Cover Letters
|
||||
|
||||
↓
|
||||
|
||||
Portfolio
|
||||
|
||||
↓
|
||||
|
||||
Interview Preparation
|
||||
|
||||
↓
|
||||
|
||||
Future Career Outputs
|
||||
|
||||
Future enhancements should strengthen this workflow, never replace it.
|
||||
|
||||
---
|
||||
|
||||
# Target Audience
|
||||
|
||||
Primary:
|
||||
|
||||
- Individual job seekers
|
||||
|
||||
Secondary:
|
||||
|
||||
- Graduates
|
||||
- Career changers
|
||||
- Software engineers
|
||||
- Technical professionals
|
||||
- Knowledge workers
|
||||
|
||||
Future:
|
||||
|
||||
- SaaS customers
|
||||
- Premium subscribers
|
||||
|
||||
There is currently no requirement for recruiter functionality.
|
||||
|
||||
---
|
||||
|
||||
# Design Philosophy
|
||||
|
||||
The application should feel:
|
||||
|
||||
- Modern
|
||||
- Professional
|
||||
- Fast
|
||||
- Calm
|
||||
- Clean
|
||||
- Premium
|
||||
- AI-native
|
||||
- Notion-inspired
|
||||
- Productivity focused
|
||||
|
||||
Primary inspirations:
|
||||
|
||||
- Notion
|
||||
- Linear
|
||||
- FlowCV
|
||||
- Jobscan
|
||||
|
||||
Avoid:
|
||||
|
||||
- clutter
|
||||
- unnecessary dialogs
|
||||
- confusing navigation
|
||||
- feature overload
|
||||
- excessive configuration
|
||||
|
||||
The UI should feel approachable while remaining powerful.
|
||||
|
||||
---
|
||||
|
||||
# Core Principles
|
||||
|
||||
## Principle 1
|
||||
|
||||
Working software is more valuable than rewrites.
|
||||
|
||||
Prefer incremental improvements.
|
||||
|
||||
---
|
||||
|
||||
## Principle 2
|
||||
|
||||
Do not rewrite large areas unless absolutely necessary.
|
||||
|
||||
Refactor gradually.
|
||||
|
||||
---
|
||||
|
||||
## Principle 3
|
||||
|
||||
Preserve backwards compatibility wherever practical.
|
||||
|
||||
---
|
||||
|
||||
## Principle 4
|
||||
|
||||
Every new feature should integrate naturally into the existing workflow.
|
||||
|
||||
Avoid bolting features onto random pages.
|
||||
|
||||
---
|
||||
|
||||
## Principle 5
|
||||
|
||||
Reduce complexity whenever possible.
|
||||
|
||||
If something can be achieved with one screen instead of three, prefer one.
|
||||
|
||||
---
|
||||
|
||||
## Principle 6
|
||||
|
||||
Everything should have one obvious place.
|
||||
|
||||
Avoid duplicated functionality.
|
||||
|
||||
---
|
||||
|
||||
# Core Workflow
|
||||
|
||||
The application revolves around a single journey.
|
||||
|
||||
Discover Job
|
||||
|
||||
↓
|
||||
|
||||
Import Job
|
||||
|
||||
↓
|
||||
|
||||
Review Job
|
||||
|
||||
↓
|
||||
|
||||
Apply
|
||||
|
||||
↓
|
||||
|
||||
Track Progress
|
||||
|
||||
↓
|
||||
|
||||
Receive Communication
|
||||
|
||||
↓
|
||||
|
||||
Prepare Interview
|
||||
|
||||
↓
|
||||
|
||||
Accept / Reject Offer
|
||||
|
||||
Career tools enhance this process.
|
||||
|
||||
---
|
||||
|
||||
# Career Workspace
|
||||
|
||||
Career Workspace is the second major feature area.
|
||||
|
||||
It should never overshadow Job Tracking.
|
||||
|
||||
Purpose:
|
||||
|
||||
Maintain one structured professional profile.
|
||||
|
||||
Generate multiple outputs.
|
||||
|
||||
Everything should originate from one source of truth.
|
||||
|
||||
---
|
||||
|
||||
# Source of Truth
|
||||
|
||||
There is exactly ONE master career profile.
|
||||
|
||||
It contains:
|
||||
|
||||
- personal information
|
||||
- experience
|
||||
- education
|
||||
- skills
|
||||
- projects
|
||||
- certifications
|
||||
- languages
|
||||
- awards
|
||||
- publications
|
||||
- organisations
|
||||
- references
|
||||
|
||||
Users may edit this manually at any time.
|
||||
|
||||
The master profile is never automatically overwritten.
|
||||
|
||||
---
|
||||
|
||||
# CV Strategy
|
||||
|
||||
The CV builder is NOT the user's data.
|
||||
|
||||
It is only a presentation layer.
|
||||
|
||||
Relationship:
|
||||
|
||||
Master Career Profile
|
||||
|
||||
↓
|
||||
|
||||
CV Builder
|
||||
|
||||
↓
|
||||
|
||||
Theme
|
||||
|
||||
↓
|
||||
|
||||
Generated CV
|
||||
|
||||
↓
|
||||
|
||||
PDF / HTML / DOCX
|
||||
|
||||
Never duplicate user data between templates.
|
||||
|
||||
---
|
||||
|
||||
# Tailored CV Strategy
|
||||
|
||||
Users create tailored CVs from their master profile.
|
||||
|
||||
Workflow:
|
||||
|
||||
Master Career Profile
|
||||
|
||||
↓
|
||||
|
||||
Job Analysis
|
||||
|
||||
↓
|
||||
|
||||
AI Suggestions
|
||||
|
||||
↓
|
||||
|
||||
User Review
|
||||
|
||||
↓
|
||||
|
||||
User Edits
|
||||
|
||||
↓
|
||||
|
||||
Save Against Application
|
||||
|
||||
↓
|
||||
|
||||
Export
|
||||
|
||||
Users may manually edit every generated CV.
|
||||
|
||||
AI never has final control.
|
||||
|
||||
---
|
||||
|
||||
# Public CVs
|
||||
|
||||
Supported feature.
|
||||
|
||||
Users can choose whether a CV is public.
|
||||
|
||||
If enabled:
|
||||
|
||||
Generate:
|
||||
|
||||
/cv/{random-guid}
|
||||
|
||||
No usernames initially.
|
||||
|
||||
Privacy first.
|
||||
|
||||
---
|
||||
|
||||
# Job Tracking
|
||||
|
||||
The application revolves around Applications.
|
||||
|
||||
Data hierarchy:
|
||||
|
||||
Company
|
||||
|
||||
↓
|
||||
|
||||
Job Opportunity
|
||||
|
||||
↓
|
||||
|
||||
Application
|
||||
|
||||
↓
|
||||
|
||||
Communication
|
||||
|
||||
↓
|
||||
|
||||
Follow Ups
|
||||
|
||||
↓
|
||||
|
||||
Documents
|
||||
|
||||
Companies are reusable.
|
||||
|
||||
Users may apply for multiple roles at the same company.
|
||||
|
||||
---
|
||||
|
||||
# Communication
|
||||
|
||||
Every application should eventually contain:
|
||||
|
||||
- Emails
|
||||
- Notes
|
||||
- Follow Ups
|
||||
- Attachments
|
||||
- Timeline
|
||||
- AI Insights
|
||||
|
||||
---
|
||||
|
||||
# User Journey
|
||||
|
||||
Ideal onboarding:
|
||||
|
||||
Register
|
||||
|
||||
↓
|
||||
|
||||
Verify Email
|
||||
|
||||
↓
|
||||
|
||||
Create Profile
|
||||
|
||||
↓
|
||||
|
||||
Import CV
|
||||
|
||||
↓
|
||||
|
||||
Review Career Profile
|
||||
|
||||
↓
|
||||
|
||||
Connect Email
|
||||
|
||||
↓
|
||||
|
||||
Import First Job
|
||||
|
||||
↓
|
||||
|
||||
Generate First CV
|
||||
|
||||
↓
|
||||
|
||||
Begin Tracking Applications
|
||||
|
||||
The onboarding experience should guide the user through this journey.
|
||||
|
||||
---
|
||||
|
||||
# Authentication
|
||||
|
||||
Supported:
|
||||
|
||||
- Email/password
|
||||
- Google
|
||||
- Microsoft
|
||||
|
||||
Future:
|
||||
|
||||
- Passkeys
|
||||
- Additional OAuth providers
|
||||
|
||||
Security features:
|
||||
|
||||
- Email verification
|
||||
- Password reset
|
||||
- 2FA
|
||||
- CAPTCHA
|
||||
- Rate limiting
|
||||
- Session management
|
||||
|
||||
---
|
||||
|
||||
# AI
|
||||
|
||||
The application supports multiple providers.
|
||||
|
||||
Architecture:
|
||||
|
||||
Provider Interface
|
||||
|
||||
↓
|
||||
|
||||
OpenAI
|
||||
|
||||
Gemini
|
||||
|
||||
Claude
|
||||
|
||||
Ollama
|
||||
|
||||
Future Providers
|
||||
|
||||
The admin controls available providers.
|
||||
|
||||
Users should never be locked into one AI model.
|
||||
|
||||
---
|
||||
|
||||
# AI Behaviour
|
||||
|
||||
AI assists.
|
||||
|
||||
Users decide.
|
||||
|
||||
AI must never silently alter important user data.
|
||||
|
||||
Every generated result should be reviewable.
|
||||
|
||||
---
|
||||
|
||||
# UI Principles
|
||||
|
||||
Every page should:
|
||||
|
||||
Have one clear purpose.
|
||||
|
||||
One primary action.
|
||||
|
||||
Consistent spacing.
|
||||
|
||||
Consistent typography.
|
||||
|
||||
Predictable navigation.
|
||||
|
||||
Avoid overwhelming new users.
|
||||
|
||||
---
|
||||
|
||||
# CV Builder Inspiration
|
||||
|
||||
Reference applications:
|
||||
|
||||
FlowCV
|
||||
|
||||
ElegantCV
|
||||
|
||||
Reactive Resume
|
||||
|
||||
Novoresume
|
||||
|
||||
Purpose:
|
||||
|
||||
Study:
|
||||
|
||||
- UX
|
||||
- Workflows
|
||||
- Features
|
||||
- Theme systems
|
||||
|
||||
Do NOT:
|
||||
|
||||
- Copy code
|
||||
- Copy branding
|
||||
- Copy assets
|
||||
|
||||
---
|
||||
|
||||
# HTML Reference
|
||||
|
||||
Reference HTML:
|
||||
|
||||
D:\FlowCV
|
||||
|
||||
For research only.
|
||||
|
||||
Never copy.
|
||||
|
||||
---
|
||||
|
||||
# Future Vision
|
||||
|
||||
The application should gradually evolve into a Career Workspace.
|
||||
|
||||
Possible future outputs:
|
||||
|
||||
- CV
|
||||
- Cover Letter
|
||||
- Public Profile
|
||||
- Portfolio
|
||||
- LinkedIn Summary
|
||||
- Interview Preparation
|
||||
- Personal Website
|
||||
|
||||
These all originate from one career profile.
|
||||
|
||||
---
|
||||
|
||||
# SaaS Readiness
|
||||
|
||||
Architecture should support:
|
||||
|
||||
- subscriptions
|
||||
- premium themes
|
||||
- AI limits
|
||||
- storage limits
|
||||
- billing
|
||||
- Stripe
|
||||
- feature flags
|
||||
|
||||
Do not over-engineer before needed.
|
||||
|
||||
Excellent UX is more important.
|
||||
|
||||
---
|
||||
|
||||
# Coding Philosophy
|
||||
|
||||
Prefer:
|
||||
|
||||
Small services
|
||||
|
||||
Small components
|
||||
|
||||
Good naming
|
||||
|
||||
Composition
|
||||
|
||||
Dependency injection
|
||||
|
||||
Testability
|
||||
|
||||
Readable code
|
||||
|
||||
Avoid:
|
||||
|
||||
Massive controllers
|
||||
|
||||
God services
|
||||
|
||||
Duplicated logic
|
||||
|
||||
Large React components
|
||||
|
||||
Magic strings
|
||||
|
||||
Hidden business logic
|
||||
|
||||
---
|
||||
|
||||
# Working Style
|
||||
|
||||
Before implementing anything:
|
||||
|
||||
1. Read relevant documentation.
|
||||
2. Analyse current implementation.
|
||||
3. Compare current state with target state.
|
||||
4. Produce a short implementation plan.
|
||||
5. Implement incrementally.
|
||||
6. Update documentation.
|
||||
7. Test thoroughly.
|
||||
|
||||
---
|
||||
|
||||
# Decision Making
|
||||
|
||||
When multiple solutions exist:
|
||||
|
||||
Choose the solution that:
|
||||
|
||||
- improves UX
|
||||
- reduces maintenance
|
||||
- keeps architecture clean
|
||||
- supports future SaaS
|
||||
- preserves current functionality
|
||||
|
||||
---
|
||||
|
||||
# What AI Should Never Do
|
||||
|
||||
Never:
|
||||
|
||||
Rewrite the application from scratch.
|
||||
|
||||
Break existing functionality.
|
||||
|
||||
Duplicate existing systems.
|
||||
|
||||
Ignore existing architecture.
|
||||
|
||||
Remove features without justification.
|
||||
|
||||
Invent requirements.
|
||||
|
||||
Ignore documentation.
|
||||
|
||||
---
|
||||
|
||||
# Documentation
|
||||
|
||||
Whenever a significant architectural decision is made:
|
||||
|
||||
Create or update an ADR.
|
||||
|
||||
Keep documentation current.
|
||||
|
||||
Documentation is considered part of the application.
|
||||
|
||||
---
|
||||
|
||||
# Final Reminder
|
||||
|
||||
Jobjakt exists to help people get jobs.
|
||||
|
||||
Every feature should ultimately help users:
|
||||
|
||||
- discover opportunities
|
||||
- apply faster
|
||||
- improve applications
|
||||
- stay organised
|
||||
- secure interviews
|
||||
- secure employment
|
||||
|
||||
If a proposed feature does not support that mission, reconsider whether it belongs in the product.
|
||||
@@ -0,0 +1,270 @@
|
||||
# Jobjakt Glossary
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines common terminology used throughout the application.
|
||||
|
||||
This prevents ambiguity between product, UX, backend, frontend, and AI development.
|
||||
|
||||
---
|
||||
|
||||
# Core Terms
|
||||
|
||||
## Job
|
||||
|
||||
A job opportunity that a user may be interested in.
|
||||
|
||||
Examples:
|
||||
|
||||
- Software Engineer role.
|
||||
- Developer position.
|
||||
- Analyst role.
|
||||
|
||||
A Job may exist before an application is submitted.
|
||||
|
||||
---
|
||||
|
||||
## Job Opportunity
|
||||
|
||||
A specific role offered by a company.
|
||||
|
||||
Contains:
|
||||
|
||||
- Job title.
|
||||
- Company.
|
||||
- Description.
|
||||
- Location.
|
||||
- Salary information.
|
||||
- Requirements.
|
||||
|
||||
---
|
||||
|
||||
## Application
|
||||
|
||||
A user's attempt to obtain a specific job.
|
||||
|
||||
Relationship:
|
||||
|
||||
Company
|
||||
|
||||
↓
|
||||
|
||||
Job Opportunity
|
||||
|
||||
↓
|
||||
|
||||
Application
|
||||
|
||||
---
|
||||
|
||||
## Application Stage
|
||||
|
||||
The current state of an application.
|
||||
|
||||
Examples:
|
||||
|
||||
- Interested
|
||||
- Preparing
|
||||
- Applied
|
||||
- Screening
|
||||
- Interview
|
||||
- Offer
|
||||
- Rejected
|
||||
- Withdrawn
|
||||
|
||||
---
|
||||
|
||||
## Company
|
||||
|
||||
An organisation offering employment.
|
||||
|
||||
Companies are reusable entities.
|
||||
|
||||
Example:
|
||||
|
||||
A user may apply for:
|
||||
|
||||
- Backend Developer
|
||||
- Frontend Developer
|
||||
|
||||
at the same company.
|
||||
|
||||
---
|
||||
|
||||
# Career Workspace
|
||||
|
||||
## Career Profile
|
||||
|
||||
The user's professional source of truth.
|
||||
|
||||
Contains:
|
||||
|
||||
- Personal details.
|
||||
- Experience.
|
||||
- Education.
|
||||
- Skills.
|
||||
- Projects.
|
||||
- Certifications.
|
||||
- Achievements.
|
||||
|
||||
The career profile is NOT a CV.
|
||||
|
||||
It is the data used to create outputs.
|
||||
|
||||
---
|
||||
|
||||
## Master CV
|
||||
|
||||
A generated representation of the Career Profile.
|
||||
|
||||
Purpose:
|
||||
|
||||
Provide a default CV.
|
||||
|
||||
It can be customised but should originate from the Career Profile.
|
||||
|
||||
---
|
||||
|
||||
## CV Variant
|
||||
|
||||
A different presentation of the same career information.
|
||||
|
||||
Examples:
|
||||
|
||||
- Software Engineer CV.
|
||||
- Management CV.
|
||||
- Academic CV.
|
||||
|
||||
Variants should not duplicate career data.
|
||||
|
||||
---
|
||||
|
||||
## Tailored CV
|
||||
|
||||
A CV adapted for a specific job application.
|
||||
|
||||
Created from:
|
||||
|
||||
Master Career Profile
|
||||
|
||||
+
|
||||
|
||||
Job Description
|
||||
|
||||
+
|
||||
|
||||
AI Suggestions
|
||||
|
||||
---
|
||||
|
||||
## CV Theme
|
||||
|
||||
The visual design system used to render a CV.
|
||||
|
||||
Contains:
|
||||
|
||||
- Layout.
|
||||
- Typography.
|
||||
- Colours.
|
||||
- Spacing.
|
||||
- Components.
|
||||
|
||||
Themes should not contain user data.
|
||||
|
||||
---
|
||||
|
||||
# AI Terms
|
||||
|
||||
## AI Provider
|
||||
|
||||
A service capable of generating AI responses.
|
||||
|
||||
Examples:
|
||||
|
||||
- OpenAI.
|
||||
- Gemini.
|
||||
- Claude.
|
||||
- Ollama.
|
||||
|
||||
---
|
||||
|
||||
## AI Generation
|
||||
|
||||
Any content created or improved by AI.
|
||||
|
||||
Examples:
|
||||
|
||||
- CV bullet improvements.
|
||||
- Cover letters.
|
||||
- Job matching.
|
||||
|
||||
---
|
||||
|
||||
## AI Suggestion
|
||||
|
||||
A recommendation from AI that requires user approval.
|
||||
|
||||
---
|
||||
|
||||
# Documents
|
||||
|
||||
## Cover Letter
|
||||
|
||||
A document explaining why a user is suitable for a specific role.
|
||||
|
||||
---
|
||||
|
||||
## Portfolio
|
||||
|
||||
A collection of professional work.
|
||||
|
||||
Examples:
|
||||
|
||||
- Projects.
|
||||
- Case studies.
|
||||
- Publications.
|
||||
|
||||
---
|
||||
|
||||
# User
|
||||
|
||||
A person using Jobjakt.
|
||||
|
||||
---
|
||||
|
||||
# Admin
|
||||
|
||||
A user with system management permissions.
|
||||
|
||||
Admins manage:
|
||||
|
||||
- AI providers.
|
||||
- System settings.
|
||||
- Feature controls.
|
||||
|
||||
---
|
||||
|
||||
# Career Workspace
|
||||
|
||||
The collection of tools supporting the user's career development.
|
||||
|
||||
Includes:
|
||||
|
||||
- Career Profile.
|
||||
- CV Builder.
|
||||
- Cover Letters.
|
||||
- Portfolio.
|
||||
- Future career tools.
|
||||
|
||||
---
|
||||
|
||||
# SaaS
|
||||
|
||||
Future commercial version of Jobjakt.
|
||||
|
||||
Potential features:
|
||||
|
||||
- Subscriptions.
|
||||
- Premium themes.
|
||||
- AI limits.
|
||||
- Storage limits.
|
||||
@@ -0,0 +1,105 @@
|
||||
# Product Principles
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the principles that guide product decisions.
|
||||
|
||||
---
|
||||
|
||||
# Principle 1
|
||||
|
||||
## Job Tracking Comes First
|
||||
|
||||
Jobjakt exists primarily to help users manage their job search.
|
||||
|
||||
Every major decision should support:
|
||||
|
||||
- Finding jobs.
|
||||
- Applying efficiently.
|
||||
- Tracking progress.
|
||||
- Improving outcomes.
|
||||
|
||||
---
|
||||
|
||||
# Principle 2
|
||||
|
||||
## Career Data Is the Foundation
|
||||
|
||||
Users should maintain one professional identity.
|
||||
|
||||
The application should avoid:
|
||||
|
||||
- Multiple disconnected profiles.
|
||||
- Re-entering information.
|
||||
- Duplicating career history.
|
||||
|
||||
---
|
||||
|
||||
# Principle 3
|
||||
|
||||
## AI Assists, Humans Decide
|
||||
|
||||
AI should:
|
||||
|
||||
- Suggest.
|
||||
- Improve.
|
||||
- Analyse.
|
||||
- Accelerate.
|
||||
|
||||
AI should not:
|
||||
|
||||
- Replace user judgement.
|
||||
- Modify facts silently.
|
||||
- Invent experience.
|
||||
|
||||
---
|
||||
|
||||
# Principle 4
|
||||
|
||||
## Simplicity Over Complexity
|
||||
|
||||
Powerful features should remain approachable.
|
||||
|
||||
Avoid:
|
||||
|
||||
- Complex workflows.
|
||||
- Excessive configuration.
|
||||
- Feature overload.
|
||||
|
||||
---
|
||||
|
||||
# Principle 5
|
||||
|
||||
## Progressive Disclosure
|
||||
|
||||
Show users what they need when they need it.
|
||||
|
||||
Example:
|
||||
|
||||
New users:
|
||||
|
||||
Simple CV creation.
|
||||
|
||||
Advanced users:
|
||||
|
||||
Custom themes, AI tools, detailed controls.
|
||||
|
||||
---
|
||||
|
||||
# Principle 6
|
||||
|
||||
## Quality Over Quantity
|
||||
|
||||
A small number of excellent features is better than many unfinished features.
|
||||
|
||||
---
|
||||
|
||||
# Principle 7
|
||||
|
||||
## SaaS Ready, Not SaaS First
|
||||
|
||||
Architecture should allow future growth.
|
||||
|
||||
However:
|
||||
|
||||
Do not sacrifice user experience today for hypothetical future requirements.
|
||||
@@ -0,0 +1,113 @@
|
||||
# Design Principles
|
||||
|
||||
## Goal
|
||||
|
||||
Create a premium, modern career management experience.
|
||||
|
||||
---
|
||||
|
||||
# Visual Direction
|
||||
|
||||
The application should feel:
|
||||
|
||||
- Professional.
|
||||
- Modern.
|
||||
- Calm.
|
||||
- Intelligent.
|
||||
- Trustworthy.
|
||||
|
||||
---
|
||||
|
||||
# Inspirations
|
||||
|
||||
## Notion
|
||||
|
||||
For:
|
||||
|
||||
- Organisation.
|
||||
- Clean interfaces.
|
||||
- Flexible content.
|
||||
|
||||
## Linear
|
||||
|
||||
For:
|
||||
|
||||
- Professional software feel.
|
||||
- Speed.
|
||||
- Keyboard-friendly workflows.
|
||||
|
||||
## FlowCV
|
||||
|
||||
For:
|
||||
|
||||
- CV creation experience.
|
||||
- Template selection.
|
||||
- Visual editing.
|
||||
|
||||
## Jobscan
|
||||
|
||||
For:
|
||||
|
||||
- Career optimisation.
|
||||
|
||||
---
|
||||
|
||||
# Navigation
|
||||
|
||||
Users should always understand:
|
||||
|
||||
Where am I?
|
||||
|
||||
What can I do?
|
||||
|
||||
What happens next?
|
||||
|
||||
---
|
||||
|
||||
# Forms
|
||||
|
||||
Forms should:
|
||||
|
||||
- Be grouped logically.
|
||||
- Explain fields.
|
||||
- Validate clearly.
|
||||
- Avoid unnecessary questions.
|
||||
|
||||
---
|
||||
|
||||
# Empty States
|
||||
|
||||
Every empty state should explain:
|
||||
|
||||
- Why it matters.
|
||||
- What action to take.
|
||||
- How to get started.
|
||||
|
||||
---
|
||||
|
||||
# Errors
|
||||
|
||||
Errors should:
|
||||
|
||||
- Explain what happened.
|
||||
- Suggest next steps.
|
||||
- Avoid technical language.
|
||||
|
||||
---
|
||||
|
||||
# Dark Mode
|
||||
|
||||
Dark mode should be supported.
|
||||
|
||||
It should feel designed, not inverted.
|
||||
|
||||
---
|
||||
|
||||
# Accessibility
|
||||
|
||||
All features should consider:
|
||||
|
||||
- Keyboard navigation.
|
||||
- Screen readers.
|
||||
- Contrast.
|
||||
- Clear focus states.
|
||||
@@ -0,0 +1,3 @@
|
||||
# overview
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# prompts
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# providers
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# safety
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# backend
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# current
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# database
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# deployment
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# frontend
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# future
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# 2fa
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# login
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# oauth
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# overview
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# signup
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# education
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# experience
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# master-profile
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# overview
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# projects
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# skills
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# ai
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# builder
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# overview
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# ai
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# content
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# customisation
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# exports
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# overview
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# rendering
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# themes
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# workflow
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# documentation-rules
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# applications
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# companies
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# followups
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# job-import
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# job-search
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# overview
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# business-model
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# goals
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# mission
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# non-goals
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# personas
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# roadmap
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# vision
|
||||
|
||||
TODO: Complete documentation.
|
||||
@@ -0,0 +1,3 @@
|
||||
# flowcv
|
||||
|
||||
TODO: Complete documentation.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user