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>
54 KiB
Jobjakt — Application Discovery Report
Date: 2026-07-17 Method: read-only audit. Every claim below verified against code at the cited path, not against documentation. Scope: full application (backend, frontend, AI sidecar, infra, docs, competitor research).
1. Executive summary
Jobjakt is a substantially more complete product than its documentation suggests, built around a genuinely differentiated core: a self-hosted, local-AI job tracker with Gmail correspondence import and a human review queue. That combination is ahead of the paid market (Teal, Huntr, Jobscan all lack inbox auto-tracking). The backend is mature — 19 controllers, ~9.3k lines of controller code, ~7.5k lines of services, 36 test classes including dedicated authorization and SSRF-guard suites, real multi-tenancy via EF global query filters, 2FA, trusted devices, session management, and a working CI/CD pipeline to production.
The gap is not capability. It is three structural mismatches between what the product says it is and what the code models:
- There is no Job entity. The only entity is
JobApplication, and the pipeline's first stage isApplied(JobTrackerApi/Services/JobPipeline.cs:24). The glossary states "A Job may exist before an application is submitted" and the target workflow is Add Job → prepare CV → prepare cover letter → submit → track. The data model cannot represent a job you have not yet applied to. This single fact blocks the top-priority workflow indocs/MASTER_IMPLEMENTATION_GUIDE.md. - The CV Builder does not exist.
CareerWorkspacePagerenders a "CV Builder" tab that passes acareerViewprop toProfilePage— and that prop is destructured and never read (job-tracker-ui/src/views/ProfilePage.tsx:231). Both tabs render identical content. CV "themes" are five hardcoded C# string-interpolated HTML functions (JobTrackerApi/Services/CvTemplateRenderer.cs:22), which cannot express any of the content/design separation the guide requires. - The documentation is a scaffold, not a description. 139 active doc files; the median is ~480 bytes — a title and one generic sentence.
docs/research/flowcv-analysis.mdopens with "TODO: Complete documentation." The four ADRs indocs/_archive/decisions/are 0 bytes. Meanwhiledocs/_archive/SYSTEM_OVERVIEW.md(20 KB) anddocs/_archive/PRODUCT_RESEARCH.md(13 KB) are excellent, evidence-based, and mostly still accurate. The real documentation was archived and replaced with stubs.
Nothing here needs a rewrite. The build order that follows is: fix the data model so the core workflow is representable, then delete the fake CV Builder and build a real one on a data-driven theme system.
Severity-ranked headline issues
| # | Issue | Impact | Evidence |
|---|---|---|---|
| 1 | No pre-application Job state; pipeline starts at Applied |
Blocks the guide's #2 priority workflow entirely | Services/JobPipeline.cs:24 |
| 2 | CV Builder tab is inert — careerView prop never read |
Ships a visible feature that does nothing | views/ProfilePage.tsx:231 |
| 3 | AI sidecar has zero auth, port 8001 published to host | Unauthenticated LLM access; burns Gemini/Groq API key | docker-compose.yml, tools/summarizer/app.py |
| 4 | DataProtection keys remain in git history | Untracked in 519c32e but recoverable; rotation still open |
git log --all -- JobTrackerApi/keys |
| 5 | Active docs are stubs; real docs archived | Every future AI session starts from fiction | wc -c docs/**/*.md |
| 6 | Two god controllers (2313 + 2249 lines) | Violates the guide's own "avoid massive controllers" rule | Controllers/JobApplications*.cs, ProfileCv*.cs |
2. Current product understanding
2.1 What it actually is
A self-hosted, multi-user job application tracker with a local-AI career assistant bolted on correctly — the AI is grounded in the user's real parsed CV rather than free-floating, which is the architecturally right answer to the "AI slop" complaint that dominates competitor reviews.
Production deployment: https://jobs.cesnimda.uk via Gitea Actions → SSH → Docker Compose.
2.2 Target users
Matches the documented personas, with one caveat: the app currently serves exactly one persona — the owner. Auth:AllowRegistration defaults to false (Controllers/AuthController.cs:135), so registration returns HTTP 403 unless explicitly enabled. Multi-tenancy is implemented (every entity carries OwnerUserId, enforced by global query filters); it is simply not open.
The job-import plugins are Norwegian-market specific: Finn, NAV, Jobbnorge, LinkedIn (Services/JobImport/Plugins/). This is a real, unstated product decision — Jobjakt is currently a Norway-focused tracker.
2.3 Current feature reality vs the stated hierarchy
The guide's hierarchy is CORE: Job Tracking → Applications → Workflow → Follow-ups → Communication, then SUPPORTING: Career Profile → Master CV → CV Builder → Cover Letters → Portfolio → Interview Prep, then ENHANCEMENT: Job Discovery.
Measured against the code:
| Layer | State |
|---|---|
| Job Tracking | ✅ Strong — but starts at "Applied", so the pre-application half is missing |
| Applications | ✅ Strong — 38 endpoints, rich lifecycle |
| Application Workflow | 🟡 The add-job wizard exists; the pipeline it feeds cannot hold a not-yet-applied job |
| Follow-ups | ✅ Strong — reminders, rules engine, auto-ghosting, AI drafts, send |
| Communication Tracking | ✅ Best-in-class — Gmail OAuth + review queue, IMAP, MS Graph |
| Career Profile | 🟡 Exists as a JSON blob, not a queryable model |
| Master CV | ✅ Upload → OCR → parse → structured profile works |
| CV Builder | ❌ Does not exist (inert tab) |
| Cover Letters | ✅ AI generation + drafts exist |
| Portfolio | ❌ Attachment slot only; no portfolio model |
| Interview Preparation | ✅ Endpoint exists (/interview-prep) |
| Job Discovery | ❌ Zero code |
The inversion risk the guide warns about has not happened. Job tracking is decisively the strongest part of the app. The CV system is the weakest. If anything, the docs over-invest in CV builder prose relative to what the product needs.
2.4 Current user journey (as actually coded)
Owner logs in (registration off)
↓
Dashboard — stats + dismissible 2-item checklist
↓
Add Job wizard: URL → preview/extract → verify → CV → cover letter → portfolio → files
↓
Job saved directly as "Applied" ← the workflow's premise is broken here
↓
Track: status / follow-ups / correspondence / AI assists
3. User journey analysis
3.1 New user journey
Current:
- Landing —
views/LandingPage.tsx(265 lines), redirects authenticated users. - Registration — endpoint exists (
POST /api/auth/register) but is 403 by default, and there is no/registerroute in the router. Sign-up is folded intoLoginPage.tsx. - Login — solid: email/password, Google, Microsoft, 2FA challenge, trusted devices.
- First setup —
OnboardingChecklist.tsx: two items (upload CV, add a job), dismissible tolocalStorage, auto-hides when both are done.
Target: Signup → Create profile → Add/import CV → Add first job → Prepare application
Missing steps:
- Signup is off and has no dedicated screen.
- No "create profile" step — the profile is a side-effect of CV upload.
- No email-verification gate in the flow (
POST /auth/verify-emailexists; nothing forces it). - No "connect email" step, despite Gmail import being the strongest differentiator — it is buried in
/settings/connected-accounts. - No "prepare application" step — the wizard ends at save.
Confusing areas:
- The onboarding checklist infers "has CV" from
profileCvTextbeing non-empty (OnboardingChecklist.tsx:31) — a text field, not the structured profile. A user with a parsed profile but empty text would be told to upload a CV again. - Dismissal is permanent per-user in
localStorage; there is no way back. /profileand/careerboth renderProfilePage, differing only by acareerOnlyboolean. Two nav destinations, one component, overlapping content — this is precisely the "everything should have one obvious place" rule being broken.
UX problems: onboarding is a checkbox list, not a guided flow. The guide asks users to always know "what happens next"; the checklist answers that twice and then never again.
3.2 Job workflow
Current — genuinely good, and better than the docs claim:
- Adding jobs —
AddJobModal.tsx(618 lines) implements a 6-step wizard:["Add job", "Review details", "CV", "Cover letter", "Portfolio", "Additional files"]with skippable optional steps (AddJobModal.tsx:368). Landed in commitaa3567d. - Importing —
POST /api/jobimport/preview→UniversalJobParser+ per-site plugins + JSON-LD fallback, language detection, skill tagging. - Editing —
EditJobDialog.tsx,JobDetailsDialog.tsx(1400 lines). - Stages — free-text
Statuscanonicalized byJobPipeline.Normalize, preserving unknown values (a good, non-destructive design). - Follow-ups —
FollowUpReminderHostedService,RulesEngine(auto-ghosting), AI drafts, send. - Communication —
Correspondenceper application; Gmail/IMAP/Graph import with review queue.
Target: Add Job → Import from URL → Extract → Verify → Choose/Create company → Add CV → Generate CV → Cover letter → Portfolio/files → Application tracking
Gap analysis — the wizard already matches the target almost exactly. Two things are missing, and one is fatal:
- Fatal: the destination stage does not exist. The wizard walks the user through preparing an application, then must save it as
Applied. There is noSaved/Interested/Preparingstage (JobPipeline.cs:24-31— stages areApplied, Waiting, Interview, Offer, Rejected, Ghosted). The wizard's whole premise — prepare before applying — has nowhere to land. Users must either lie about having applied or not use the wizard as intended. - "Generate CV if needed" is absent from the wizard. Generation exists (
POST /jobapplications/{id}/generate-tailored-cv-draft) but only after the job exists, from the job detail view. The wizard's CV step is an upload field only. - Import is preview-only.
JobImportControllerexposes exactly one endpoint (/preview). Persistence goes through the generic create path. This is fine, but it means no import history, no re-import, no dedup at import time (dedup exists separately at/duplicate-check).
Company selection is handled — CompaniesController + reusable Company entity, matching the documented Company → Job → Application intent at the company level.
3.3 Career workflow
Current:
- Profile —
ProfilePage.tsx(1368 lines): personal details, avatar with cropping, CV upload, structured profile editing, 2FA/session cards. - CV — upload →
CvUploadArtifact→CvExtractionRun(OCR/parse, versioned with parser/normalizer/prompt versions — a genuinely good audit design) →StructuredCvProfileJSON → applied toApplicationUser.ProfileCvStructureJson. - AI — rewrite-section, rewrite-preview, improve, parse, reprocess, rebuild, export-pdf.
Target: Profile → Master Career Profile → Master CV → CV Builder → Tailored CVs → Cover Letters
Gaps:
- Master Career Profile is a JSON blob on the Identity user (
Models/ApplicationUser.cs:9). It works and is defensible for a single-writer app, but: skills are not queryable, there is no per-section version history at the DB level, and two concurrent editors clobber each other wholesale. TheStructuredCvProfileshape covers Summary, Jobs, Education, Certifications, Projects, Skills, Languages, Interests, OtherSections — the guide additionally names Achievements/Awards, Publications, Organisations, References, which have no home beyond genericOtherSections. - Master CV vs Career Profile are conflated. The glossary is explicit that "The career profile is NOT a CV" and Master CV is a generated representation. In code there is one blob and no generated-master concept.
- CV Builder — missing entirely (§10).
- Tailored CVs — this part works well.
TailoredCvDraftis a separate entity per application, and the master is never auto-overwritten. The single most important documented invariant is correctly implemented. - CV Variants (Software Engineer CV / Management CV) — not modelled. Only per-application tailored drafts exist.
4. Architecture overview
4.1 Backend
- Framework: ASP.NET Core net9.0, EF Core 9, ASP.NET Identity.
- Projects — the layout is unusual and must be understood before touching it:
| Project | Role |
|---|---|
JobTrackerApi/ |
Web host only — Program.cs, appsettings, migrations, Dockerfile. Its csproj excludes its own Controllers/** and Services/**. |
JobTrackerBackend/ |
"Transitional shared-backend" library that link-compiles ../Data, ../Models, ../JobTrackerApi/Controllers, ../JobTrackerApi/Services via <Compile Include>. Exists so tests can reference controllers without the web host. |
JobTrackerApi.Tests/ |
xUnit, 36 test classes. |
Models/, Data/ (repo root) |
The real EF models and JobTrackerContext. |
The files live in one place and compile from another. Any tooling that assumes csproj-adjacent source will mislead you.
-
Controllers (19): god-object problem is real and violates the guide's own rule —
Controller Lines JobApplicationsController2313 ProfileCvController2249 GmailController1023 AuthController879 JobApplicationsControlleralone exposes 38 endpoints. -
Services (~40): notable —
StartupInitializationExtensions(1356 lines, a second god object),SummarizerService(671),GmailOAuthService(655),JobApplicationHelpers(622),MicrosoftGraphOAuthService(507),CvTemplateRenderer(448). -
Authentication: "smart" policy scheme routes by token issuer — Google ID tokens →
googlehandler, else →localJWT (symmetric key, issuer/audience validated). Cookie sessions (jobtracker_auth, HttpOnly, SameSite=Lax) with CSRF double-submit enforcement on mutating requests.Auth:Require=truein prod sets authorize-all fallback. Local tokens must carry a subject claim — hardened after finding M013-2. -
Database: SQLite default; MariaDB/MySQL switchable via
Database:Provider. 10 migrations. -
Background jobs:
RulesHostedService,FollowUpReminderHostedService,DailyExportHostedService,JobEnrichmentHostedService,DatabaseBackupHostedService,CvProcessingQueue,SummarizerProbeHostedService. -
External integrations: Google OAuth/Gmail API, Microsoft Graph, IMAP, SMTP, LibreTranslate (optional), job sites (Finn/NAV/LinkedIn/Jobbnorge), Playwright (PDF).
-
AI integration: not in-process. The backend HTTP-calls a FastAPI sidecar (
tools/summarizer/app.py, 866 lines) exposing/health,/cv/normalize,/cv/classify-block,/cv/rewrite,/summarize,/extract-text. The sidecar picks a provider from a process-wide env varAI_PROVIDER∈ {ollama,gemini,groq}.The documented AI architecture does not exist.
docs/00-ai-context.mddescribes a provider interface fanning out to OpenAI / Gemini / Claude / Ollama, admin-controlled, with users never locked to one model. Reality: one env var, one provider per deployment, no OpenAI, no Claude, no admin control, no per-user selection.
4.2 Frontend
- Framework: Next.js 16 + React 19 + TypeScript 5.9 + MUI 7.
- Routing — three stacks coexist. Next.js App Router (
app/layout.tsx,app/page.tsx) is a shell that mounts a client-sidereact-router-domv6 app.App.tsxuses bothcreateBrowserRouter(public routes) and a nested<Routes>inside a catch-allShell(authenticated routes). Meanwhilereact-scripts5.0.1 is still a dependency, used solely as the test runner ("test": "react-scripts test"). This is a CSR lift-and-shift: none of Next's SSR/routing/data value is realized, and the app pays for three toolchains. A known dev-only 404 on deep links follows directly from this. - Components (~34): largest are
JobDetailsDialog(1400),JobTable(786),Correspondence(732),DashboardView(666),AddJobModal(618). - State management: none. No Redux/Zustand/React Query. Local
useState+axiosper component, with a hand-rolledrefreshTokencounter threaded through props. Two workspace-cache hooks exist (components/job-workspace/useWorkspaceTabCache.ts,useJobWorkspaceBaseData.ts). For an app of this size, the absence of a server-cache layer is the main driver of the large components. - Styling: MUI
sx+ a customtheme.ts(439 lines), light/dark. Consistent, but design tokens live in component-levelsxprops (e.g. the identicalboxShadow: "0px 8px 24px -12px rgba(15,23,42,0.12)"is repeated inline across pages). - Major pages: Dashboard, Jobs, Kanban, Reminders, Companies, Correspondence (+review), Profile, Career, Settings (+connected accounts), Admin (audit/users/system), Trash.
- i18n: custom provider, EN + NB, 2130-line translation file.
- Design system: exists as a theme, not as a component library. There is no Storybook, no primitives layer.
4.3 Database
Entities (16 DbSets + Identity): Company, JobApplication, Correspondence, GmailConnection, GmailReviewDecision, MicrosoftGraphConnection, ImapConnection, Attachment, RuleSettings, UserRuleSettings, SystemEmailSettings, JobEvent, CvUploadArtifact, CvExtractionRun, TailoredCvDraft, TwoFactorRecoveryCode, TrustedDevice, UserSession.
Relationships: Company 1—* JobApplication 1—* {Correspondence, Attachment, JobEvent}, JobApplication 1—1 TailoredCvDraft, ApplicationUser 1—* {CvUploadArtifact, CvExtractionRun}.
Data duplication / debt:
JobApplicationconflates Job and Application — 43 members mixing opportunity data (JobTitle,Description,JobUrl,Salary*,Deadline,Tags) with application data (Status,DateApplied,ResponseReceived,FollowUpAt). Applying twice to the same reposted role duplicates the whole description. This is issue #1.Has*denormalized flags (HasResume,HasCoverLetter,HasPortfolio,HasOtherAttachment) duplicate whatAttachmentsalready says; there is a dedicatedAttachmentFlagsRecomputeTestssuite to keep them honest — a test that exists only because of the duplication.Tagsis a JSON array in a string column — not queryable;/tagsand/tag-trendsmust scan.- CV text stored three times:
CvExtractionRun.RawExtractedText,.NormalizedText,.StructuredProfileJson, plusApplicationUser.ProfileCvTextand.ProfileCvStructureJson. Deliberate (audit trail), but unbounded — no retention policy on extraction runs. AvatarImageDataUrl— base64 image in a DB column on the hot/auth/mepath.Statusis free-text — canonicalization is application-layer only; the DB accepts anything.
Migrations: only 10, with a SyncModelSnapshot migration — indicating the model drifted from migrations and was reconciled in bulk rather than incrementally.
4.4 Infrastructure
- Docker: 4 services —
backend,frontend(nginx),ai-service,ollama(opt-in viabundled-ollamaprofile, GPU). Externaljobtracker_sharednetwork. - Deployment: Gitea Actions → SSH →
deploy/deploy.sh→ Docker Compose on the prod host. - CI/CD:
.gitea/workflows/ci-deploy.yml. Builds backend, runs all backend tests, runs the whole frontend suite, builds frontend. Notably the file carries a comment forbidding re-introducing the old test whitelist ("the previous whitelist silently skipped new suites and let two regressions reach main") — that debt is paid. The workflow is heavily defended with retries against a flaky self-hosted runner (dotnet install,npm ciSIGSEGV, build OOM). There is no GitHub Actions workflow — do not look in.github/. - Environment variables: extensive,
.envcorrectly gitignored,.env.examplepresent. - External services: Google, Microsoft, Ollama/Gemini/Groq, SMTP, LibreTranslate.
5. Feature inventory
5.1 Working
| Feature | Purpose | Location | Problems | Recommendation |
|---|---|---|---|---|
| Job/application CRUD | Core tracking | Controllers/JobApplicationsController.cs |
2313 lines, 38 endpoints | Split by concern (CRUD / AI / analytics / drafts) |
| Status pipeline | Lifecycle | Services/JobPipeline.cs |
Starts at Applied |
Add pre-application stages — issue #1 |
| Kanban board | Visual pipeline | components/KanbanBoard.tsx (285) |
— | Keep |
| Job table + saved views | List/filter | components/JobTable.tsx (786) |
Large | Keep; extract later |
| Companies | Reusable orgs | Controllers/CompaniesController.cs |
No people/contacts entity | Add contacts (competitors have it) |
| Job import from URL | Reduce entry | Services/JobImport/ + 4 plugins |
Preview-only; NO-market only | Keep; add browser extension later |
| Add-job wizard | Guided flow | components/AddJobModal.tsx |
Lands in Applied |
Fix once #1 lands |
| Gmail import + review queue | Auto-tracking | Controllers/GmailController.cs, views/GmailReviewPage.tsx |
1023-line controller | Protect — best differentiator |
| IMAP / MS Graph mail | Auto-tracking | Services/ImapService.cs, MicrosoftGraphOAuthService.cs |
— | Keep |
| Correspondence log | Comms tracking | Models/Correspondence.cs, components/Correspondence.tsx |
— | Keep |
| Follow-up reminders | Never forget | Services/FollowUpReminderHostedService.cs |
— | Keep |
| Rules engine (auto-ghost) | Automation | Services/RulesEngine.cs |
— | Keep |
| Attachments | Files per app | Controllers/AttachmentsController.cs |
Denormalized Has* flags |
Keep; drop flags later |
| CV upload → OCR → parse | Master CV | Controllers/ProfileCvController.cs, sidecar |
2249-line controller | Split |
| Structured CV profile | Source of truth | Models/StructuredCvProfile.cs |
JSON blob; missing awards/publications/refs | Relational later |
| Extraction run audit | Traceability | Models/CvExtraction.cs |
No retention policy | Add TTL |
| Tailored CV drafts | Per-job CV | Models/TailoredCvDraft.cs |
— | Correct — master never overwritten |
| CV PDF export | Deliverable | Services/PlaywrightCvPdfExporter.cs |
— | Keep |
| Match score / candidate fit | Job↔CV fit | Services/JobCvMatchService.cs |
— | Keep — top paid feature elsewhere |
| Interview prep | Prep | /jobapplications/{id}/interview-prep |
Not surfaced as a hub | Give it a screen |
| Focus plan / readiness | Guidance | JobApplicationsController |
— | Keep |
| AI cover letters / follow-up drafts | Reduce effort | JobApplicationsController |
— | Keep |
| Application package generation | Bundle | /generate-application-package |
— | Keep |
| Analytics | Insight | Services/AnalyticsService.cs, StageAnalytics.cs |
Basic | Expand (funnel, time-in-stage) |
| Auth: password/Google/MS | Access | Controllers/AuthController.cs |
879 lines; registration off | Split; open signup for SaaS |
| 2FA + recovery codes | Security | Controllers/TwoFactorController.cs |
— | Keep |
| Trusted devices | UX | Services/TrustedDeviceService.cs |
— | Keep |
| Session management | Security | Controllers/SessionsController.cs |
— | Keep |
| Rate limiting | Abuse | Program.cs:373 (login 10, email 5, 2FA 5) |
No captcha | Add captcha before public signup |
| Multi-tenancy | Isolation | Data/JobTrackerContext.cs global filters |
— | Correct — deny-on-null |
| Admin (users/audit/system) | Ops | Controllers/Admin*.cs |
— | Keep |
| DB backup + daily export | Durability | Services/DatabaseBackup*.cs |
— | Keep |
| i18n EN/NB | Reach | src/i18n/ |
— | Keep |
| Dark mode | UX | src/theme.ts |
— | Keep |
5.2 Partial
| Feature | State | Location | Problem | Recommendation |
|---|---|---|---|---|
| Career Workspace | Shell only | views/CareerWorkspacePage.tsx (36 lines) |
Tab wrapper around ProfilePage |
Make it a real workspace |
| CV Builder | Inert | views/ProfilePage.tsx:231 |
careerView never read — both tabs identical |
Delete tab or build it |
| CV themes | 5 hardcoded | Services/CvTemplateRenderer.cs:22 |
C# string HTML: ats-minimal, harvard, auckland, edinburgh, monarch, fjord. No user customisation |
Data-driven theme model |
| Onboarding | 2-item checklist | components/OnboardingChecklist.tsx |
Not a flow; infers CV from text field | Real guided flow |
| Registration | Endpoint only | AuthController.cs:135 |
403 by default; no /register route |
Needed for SaaS |
| Portfolio | Attachment slot | AddJobModal step 4 |
No model, no page | Model it |
| Analytics | Basic stats | AnalyticsService.cs |
No funnel/response-rate/time-in-stage | Expand |
| Profile vs Career | Duplicated | /profile and /career → same component |
Two doors, one room | Separate concerns |
5.3 Planned (documented, zero code)
| Feature | Documented in | Reality |
|---|---|---|
| Job Discovery / search | docs/jobs/job-search.md, guide Phase 4 |
No code. Grep for JobSearch|discovery in backend → nothing |
Public CV (/cv/{guid}) |
docs/00-ai-context.md, docs/career/public-profile.md |
No code. No route, no IsPublic, no slug |
| SaaS: billing/subscriptions/quotas | docs/product/business-model.md (52 bytes), guide |
No code. No Stripe, no plan, no quota, no usage tracking |
| Multi-provider AI w/ admin control | docs/00-ai-context.md |
Single env var; no OpenAI/Claude; no admin UI |
| CAPTCHA | docs/00-ai-context.md |
No code |
| Passkeys | docs/00-ai-context.md |
No code |
| CV variants (SWE CV / Mgmt CV) | docs/01-glossary.md |
No code — only per-app drafts |
| Interview prep hub | Guide | Endpoint exists; no screen |
| Calendar / ICS | docs/_archive/PRODUCT_RESEARCH.md |
No code |
| Awards / Publications / Organisations / References | docs/00-ai-context.md |
Not in StructuredCvProfile; only generic OtherSections |
6. UI/UX review
Note on mockups: the discovery brief says "compare against existing mockups". No mockups exist in this repo. Prior sessions recorded a mockup set at F:\Pictures\website\jobtracker\new — not verified in this audit (out of scope, drive not read). Any redesign work must confirm that source first.
Page-by-page
| Page | Assessment |
|---|---|
Dashboard (DashboardView.tsx, 666) |
Matches vision reasonably. Stats + reminders + onboarding. Problem: 666 lines, no data layer. |
Navigation (layout/AppShell.tsx, 499) |
Mostly clean. Problem: /profile and /career are two entries rendering one component — violates "one obvious place". |
Authentication (LoginPage.tsx, 260) |
Solid and complete. Problem: no dedicated signup screen; sign-up hidden inside login. |
Settings (SettingsView.tsx) |
Recently improved — connected accounts split out (d7d7e70). Good. |
Jobs (JobTable.tsx 786 / KanbanBoard.tsx 285) |
Strong. Saved views, columns, tags. Matches vision. |
Profile (ProfilePage.tsx, 1368) |
Needs redesign. 1368 lines doing profile + CV + AI + 2FA + sessions, forked by careerOnly. This is the worst file in the frontend. |
CV Builder (CareerWorkspacePage.tsx, 36) |
Missing. Tab exists, gated on hasMasterCv, and does nothing when enabled. |
Upload flows (ProfilePage, AddJobModal) |
Work. Async CV processing via CvProcessingQueue is right. |
Requiring redesign
ProfilePage— split profile / career / security.CareerWorkspacePage— currently a facade.- Onboarding — checklist → guided flow.
JobDetailsDialog(1400) — a dialog carrying an entire workspace.
Matching current vision
Dashboard, Jobs table, Kanban, Settings, Correspondence inbox + Gmail review, Login.
Missing screens
Signup, CV Builder (content + customise + preview + export), Public CV, Job Discovery, Interview Prep hub, Portfolio, Billing/plan.
7. FlowCV comparison
Analysed the local downloads at D:\FlowCV (Resume _ FlowCV.html, _customize.html, _overview.html). Text extracted from markup; no code, markup, assets, or branding copied.
FlowCV's structure
-
Top-level tabs:
Overview | Content | Customize | AI Tools, withDownloadalways present. -
Overview: "My Resumes" list. "Your first resume is free forever. Need more than one resume? Upgrade your plan" — the free/paid line is drawn at resume count.
-
Content: section-based editor, drag-and-drop entries (with documented keyboard drag affordances — accessible), "Add Entry" / "Add Content".
-
Customize: a deep, fully data-driven control panel:
Group Controls Document Settings Language, Date Format, Page Format (A4) Templates "Update your entire resume design with one click", browse Layout Columns (One/Two/Mix), Header Position (Top/Left/Right), section order, page breaks, column width (44%/56%) Font Size Base (10pt) + deltas: Full Name +14pt, Section Headings +2pt, Entry Header +0pt Spacing Line height 1.3, space between elements, L/R margin 18mm, T/B margin 16mm Entries Structure (Full Width/Columns), Date & Location position (Right/Below Title) Headings Capitalization (Capitalize/Uppercase), icons (None/Outline/Filled) Font Body font (e.g. Zilla Slab), Name font (same/different) Colors Area (Full Page/Column/Border), mode (Single/Multi/Image), accent applied per-element (Name, Job title, Headings, Header icons, Dots/bars, Dates, Entry subtitle, Link icons) Header Text alignment, details arrangement (Icon/Bullet/Bar), 7 icon styles Photo / Links / Footer / Sections — -
Preview: live, continuous, beside the editor.
-
Export: persistent Download button — never a mode you enter.
-
Notably: FlowCV also ships a Job Tracker. It is not a pure CV tool; it is a direct competitor converging on Jobjakt's territory from the CV side.
Jobjakt vs FlowCV
| Dimension | FlowCV | Jobjakt |
|---|---|---|
| Content/design separation | Total — content is data, design is config | None — design is C# code |
| Templates | Browse + one-click swap | 6 IDs in a switch (CvTemplateRenderer.cs:22) |
| User customisation | ~40 controls | Zero |
| Preview | Live, side-by-side | Server round-trip → HTML/PDF |
| Export | Always-available button | POST /export-pdf via Playwright |
| Multi-resume | Yes (paywalled at 2+) | No variants; only per-app drafts |
| Job tracking | Bolted on | Core, and far deeper |
| AI grounded in real career data | Generic "AI Tools" | Yes — structured profile |
| Self-hosted / private | No | Yes |
What Jobjakt must adapt (not copy)
- The theme system must become data, not code. This is the single highest-leverage lesson.
CvTemplateRendererrenders by string-interpolating HTML inside a C#switch— it can never express "base font 10pt, headings +2pt, accent applies to Name and Dates but not Headings". Model themes as aCvThemedocument (layout, columns, header position, font family/sizes as base+deltas, spacing, colour targets, icon style) and render one parameterized template from it. Five hardcoded renderers are five things to maintain and zero things a user can adjust. Overview | Content | Customize | Preview/Exportis the right tab spine — and it maps onto Jobjakt's statedContent Tab → Customise Tab → Preview → Export.- Live preview beside the editor, not behind a request.
- Drag-and-drop section ordering with keyboard support — FlowCV's accessibility affordances are worth matching.
What Jobjakt must NOT copy
- The paywall shape. FlowCV gates at resume count — the exact "free tier caps at the point of seriousness" frustration documented in
docs/_archive/PRODUCT_RESEARCH.md. Jobjakt's differentiator is privacy + self-hosting; gating CV count would surrender it. - CV-first framing. FlowCV is a CV tool that added a tracker. Jobjakt is a tracker; the guide is explicit — "Do not transform Jobjakt into a CV generator."
- Control sprawl. ~40 customisation controls contradicts "avoid excessive configuration". Ship the ~12 that matter (template, columns, header position, accent, base font size, font family, spacing, margins, photo on/off, section order, date position, heading case).
- Any code, markup, asset, or branding.
8. Competitor alignment
⚠️ CORRECTION (2026-07-17, during Phase 0)
The claim below — that Novoresume/Reactive Resume/ElegantCV are not analysed in this repo — is WRONG. It was true of
mainand ofdocs/_archive/, which is all this audit searched. It is not true of the repository.A
feature/career-workspacebranch exists (local and onorigin, 10 commits, unmerged, last touched 2026-07-12) carrying:
docs/cv-builder-competitor-deep-research.md(327 lines) — deep teardowns of Novoresume, Reactive Resume, FlowCV, Teal, Enhancv, Canva, Resume.io, Kickresume, with a feature matrix and a business-model analysis.docs/cv-builder-product-teardown.md(321 lines)docs/career-workspace-product-strategy.md(328 lines) — vision, positioning, four personas.docs/career-workspace-implementation-roadmap.md(124 lines) — an ADR-grade plan with phases F0–F5, F0–F2 marked shipped.Its conclusions independently reach the same findings as §7 and §10 of this report — "the winning editor model is structured-form + live preview, not canvas", client-side preview is a hard requirement, and themes must be declarative data. That is corroboration, not duplication.
The audit's method was too narrow: I searched the working tree and
docs/, nevergit branch -a. See §13 and the Phase 0 report for the full consequences. Roadmap task 4.9 ("research Reactive Resume") is therefore already done — on that branch.
docs/research/ on main is unusable for this step. Every file is a stub; flowcv-analysis.md literally begins "TODO: Complete documentation." The archived docs/_archive/09-research/*.md are 0 bytes. The only competitor research reachable from main is docs/_archive/PRODUCT_RESEARCH.md (13 KB, sourced, dated 2026-07-02) — which covers the tracker market (Teal, Huntr, Simplify, Jobscan, OSS self-hosted), not the CV-builder market the brief asks about.
Therefore: Novoresume, Reactive Resume, and ElegantCV are not analysed in this repo at all. Superseded — see the correction above. They are analysed, on feature/career-workspace.
From FlowCV (verified) + PRODUCT_RESEARCH.md (archived, spot-checked):
What they do better:
- Data-driven theme/customisation systems (FlowCV, Reactive Resume) — Jobjakt has none.
- Live side-by-side preview.
- Browser extension capture (Teal, Huntr, Simplify) — Jobjakt is server-side parse only.
- Deep analytics: funnel, response rate, time-in-stage (Teal/Huntr premium) — Jobjakt has basic stats.
- Contact/people CRM (Teal, Huntr) — Jobjakt is company-level only.
- Interview scheduling + calendar (emerging) — Jobjakt has none.
What Jobjakt does better (protect these):
- Inbox auto-tracking with a human review queue — Teal ❌, Huntr ❌, Simplify 🟡. This is ahead of the paid market.
- Local-AI tailored CVs grounded in the user's real structured CV — directly answers the #1 competitor complaint ("AI slop / hallucinated skills").
- Rules engine + auto-ghosting — richer than most.
- Self-hosted privacy — the stated moat; every OSS alternative (JobSync, CareerSync, career-ops) is far less complete.
- AI features free and local where competitors charge $29–50/mo.
What should not be copied: count-based paywalls; autofill-at-scale / auto-apply (LazyApply, LoopCV — spray-and-pray contradicts "apply to more suitable jobs"); job-board ambitions (explicit non-goal); generic AI chat (explicit non-goal).
9. Architecture assessment
9.1 Career Workspace — ❌ Not supported
| Requirement | Verdict |
|---|---|
| Master career profile | 🟡 Exists as a JSON blob on ApplicationUser. Works; not queryable; no section-level history; missing awards/publications/organisations/references |
| CV versions | ❌ No variant model. TailoredCvDraft is per-application only. Nothing satisfies the glossary's "CV Variant" |
| CV themes | ❌ Hardcoded C# renderers. No theme entity, no user customisation. Cannot be extended without a rewrite of CvTemplateRenderer |
| Public CV | ❌ Zero code. No route, no IsPublic, no slug, no anonymous read path |
Verdict: the current system does not support the Career Workspace as documented, and cannot be incrementally coaxed into it. The theme system in particular is a structural dead end — it is not a matter of adding templates.
9.2 AI — ✅ Largely supported
| Requirement | Verdict |
|---|---|
| CV generation | ✅ Parse → structure → tailor → render → PDF, all working |
| Job matching | ✅ JobCvMatchService, /match-score, /candidate-fit |
| Cover letters | ✅ Generation + drafts |
| Interview preparation | ✅ Endpoint exists — but no UI surface |
AI is the healthiest supporting area. Grounding in the structured profile is the right architecture, and "AI never has final control" is genuinely upheld (drafts are separate entities; the master is never auto-written).
Caveat: the provider layer does not match its documentation (§4.1) and has no cost control, quota, or per-user selection. docs/ai/cost-control.md is 380 bytes of nothing; with AI_PROVIDER=gemini and an unauthenticated sidecar, there is no ceiling on spend.
9.3 SaaS — ❌ Not supported
| Requirement | Verdict |
|---|---|
| Multiple users | ✅ Genuinely solid. OwnerUserId everywhere + deny-on-null global query filters + a dedicated authorization test suite. The hard part is done |
| Premium features | ❌ No plan/tier/entitlement concept anywhere |
| Usage limits | ❌ No quota, no usage tracking. AI is unmetered — the direct cost risk |
| Storage | ❌ No limits. Attachments, CV artifacts, extraction runs, base64 avatars all unbounded |
Also blocking: registration is off by default and has no UI; there is no CAPTCHA; there is no billing integration of any kind.
Verdict: multi-tenancy is ready; commercialisation is not started. That is the right order — but nothing should be built here until the core workflow (§9.1, issue #1) is fixed.
10. CV builder assessment
The CV Builder does not exist. This is the clearest finding in the audit.
Evidence:
views/CareerWorkspacePage.tsx:25renders<Tab value="builder" label="CV Builder" disabled={!hasMasterCv} />.:31passescareerView={tab}into<ProfilePage careerOnly ... />.views/ProfilePage.tsx:231destructurescareerView = "master", and:235types it — and the identifier appears nowhere else in the codebase. Verified:grep -rn "careerView" job-tracker-ui/src/returns exactly three lines — the call site and the two declarations.- Therefore selecting "CV Builder" re-renders identical content. The tab is a no-op that looks like a feature.
Both files (CareerWorkspacePage.tsx, ProfilePage.tsx) are uncommitted working-tree changes — this is in-progress work, not shipped deception. But as it stands the tab ships a promise the code does not keep.
What does exist: a template-driven renderer — POST /profile-cv/rewrite-preview, GET /profile-cv/templates, POST /profile-cv/export-pdf, and ProfilePage.tsx:970 ("Template-driven CV builder") / :1197 ("Choose a template and generate a live preview"). That is template selection plus a server round-trip, not a builder.
What is missing against docs/MASTER_IMPLEMENTATION_GUIDE.md:312 (Content Tab → Customise Tab → Preview → Export):
- No Content tab (section add/remove/reorder, entry-level editing).
- No Customise tab (nothing is customisable — no accent, no font, no layout, no photo toggle, no section config).
- No live preview (server request required).
- Export exists.
Structural blocker. CvTemplateRenderer.Render is:
templateId switch {
"harvard" => RenderHarvard(...),
"auckland" => RenderSidebar(..., roundedPhoto: false, curvedHeader: false),
"edinburgh" => RenderSidebar(..., roundedPhoto: true, curvedHeader: true),
...
}
Design decisions are C# method parameters and interpolated CSS strings. The guide requires themes supporting "Accent colours, Typography, Layout, Spacing, Photo options, Icons" — none of which can be expressed here without adding a boolean parameter per option and multiplying the switch. roundedPhoto/curvedHeader are already that pattern starting.
Recommendation: do not extend CvTemplateRenderer. Replace it with a CvTheme value object (layout, columns, header position, font family, base size + per-element deltas, spacing, margins, accent + application targets, icon style, photo settings) rendered by one parameterized template. Ship 3–5 themes as seeded theme documents, per the guide's "3-5 excellent themes". Delete the careerView prop or wire it — do not ship the inert tab.
11. Technical debt
Ranked by cost-to-carry:
JobApplicationconflates Job and Application — blocks the primary workflow. Everything else on this list is cosmetic by comparison.- God controllers —
JobApplicationsController2313 / 38 endpoints;ProfileCvController2249;GmailController1023;AuthController879;StartupInitializationExtensions1356. The guide's own "Coding Philosophy" forbids exactly this. - Dead
careerViewprop — a shipped no-op tab. - Hardcoded CV templates — dead-end for the entire Phase 4.
- Three frontend stacks — Next.js App Router shell + react-router v6 (×2 patterns:
createBrowserRouterand nested<Routes>) + react-scripts as test runner. Pays three toolchain costs for one CSR app; causes the known dev-only deep-link 404. - No frontend data layer — no server-cache library; hand-rolled
refreshTokencounters threaded via props. Root cause of the 600–1400-line components. ProfilePageat 1368 lines forked by a boolean, serving two routes.- Denormalized
Has*flags — kept honest only by a dedicated test suite. Tagsas a JSON string column — unqueryable; forces scans in/tags,/tag-trends.- Unbounded CV text/artifact storage — no retention on
CvExtractionRun; three copies of every CV. AvatarImageDataUrl— base64 blob in a DB column on the/auth/mehot path.- Only 10 migrations + a
SyncModelSnapshot— model drifted from migrations, reconciled in bulk. JobTrackerBackendlink-compilation — source lives in one place, compiles from another. Self-described as "transitional". Surprising to every new contributor and every tool.Statusfree-text at the DB level — canonicalization is application-only.- Documentation debt (see §13) — 139 stub files masquerading as documentation; 4 zero-byte ADRs.
12. Security review
Prior work is real and good: docs/_archive/SECURITY_REPORT.md plus M013 (adversarial), M014 (remediation verification), M015 (authorization replay). Findings were fixed, not just filed — LocalAuthIdentity subject-claim enforcement and deny-on-null query filters both trace to M013-2.
Strong
- Multi-tenancy: global query filters
CurrentUserId != null && OwnerUserId == CurrentUserId— deny-on-null, the correct default. Covered byJobApplicationsAuthorizationTests,OwnershipGuardTests. - CSRF: double-submit cookie+header on mutating requests.
- Session cookie: HttpOnly, SameSite=Lax, Secure-configurable.
- Auth fails closed when required but unconfigured.
- Rate limiting: login 10/window, email 5, 2FA challenge 5.
- 2FA: TOTP, encrypted secrets, recovery codes, trusted devices.
- SSRF guard on IMAP — with a dedicated test (
ImapServiceSsrfGuardTests). - OpenAPI dev-only.
- Secrets:
.envgitignored; DP keys and runtime exports untracked in519c32e.
Open findings
| Sev | Finding | Detail |
|---|---|---|
| High | AI sidecar unauthenticated + published | docker-compose.yml maps "8001:8001" to the host. tools/summarizer/app.py has no auth — no Depends, no API key, no token check (the only Authorization header in the file is outbound to Groq, line 480). Anyone reaching the host can call /cv/rewrite, /summarize, /extract-text — burning the Gemini/Groq key and running arbitrary text through the model. Recommendation: drop the host port mapping (expose: only — the backend reaches it on the compose network), and add a shared-secret header. Do not defer this if AI_PROVIDER=gemini or groq in production. |
| Medium | DataProtection keys in git history | Untracked in 519c32e, but git log --all -- JobTrackerApi/keys still returns commits — the keys remain recoverable from history. Flagged for rotation in the 2026-07-03 report; still open. Rotate. |
| Medium | No AI cost ceiling | No quota, no usage tracking, no per-user metering. Combined with the finding above, spend is unbounded. |
| Low | No CAPTCHA | Verified absent. Rate limiting only. Acceptable while registration is closed; required before opening signup. |
| Low | Unbounded storage | Attachments, CV artifacts, extraction runs, base64 avatars — no limits, no retention. |
| Low | SameSite=Lax |
Appropriate for the OAuth redirect flows in use; noted, not a defect. |
Not verified in this audit
Dependency CVEs (no npm audit / dotnet list package --vulnerable run — CI explicitly disables audit via npm_config_audit: 'false'). Recommend a scheduled scan.
13. Documentation assessment
This deserves its own section because it actively misleads.
Active docs (docs/, 139 files): the median file is ~480 bytes — a title and one generic sentence. Examples: docs/auth/login.md = 43 bytes. docs/product/business-model.md = 52 bytes. docs/cv-builder/themes.md = 44 bytes. docs/research/flowcv-analysis.md opens with "TODO: Complete documentation."
Archive (docs/_archive/, 344 KB): contains the genuinely good work — SYSTEM_OVERVIEW.md (20 KB, mermaid architecture, verified against a named commit), PRODUCT_RESEARCH.md (13 KB, sourced competitor analysis with links), SECURITY_REPORT.md, M013–M015 assessments, performance/memory-leak analyses.
The real documentation was archived and replaced with stubs. docs/AI_SESSION_START.md instructs every future AI session to read docs/product/, docs/architecture/, docs/security/, docs/technical/ — all stubs. An assistant following its own instructions learns nothing true and confidently builds on fiction. This is the highest-leverage cheap fix in the report.
The four ADRs are 0 bytes: ADR-001-master-career-profile, ADR-002-job-application-model, ADR-003-cv-rendering, ADR-004-ai-provider-system. Their titles name the four most consequential decisions in the system — and every one is undocumented. ADR-002 in particular would have recorded whether the Job/Application conflation was deliberate.
Archive triage
Restore to active (verify first — noted deltas below):
| File | Status | Deltas since 2026-07-02 |
|---|---|---|
SYSTEM_OVERVIEW.md |
Excellent — restore as docs/architecture/current.md |
Now stale on 5 points: (1) frontend is Next.js 16 + TS 5.9, not CRA + TS 4.9; (2) root Controller/ dead folder is removed; (3) CI runs the whole frontend suite — the whitelist is gone; (4) match-score now exists; (5) salary is now structured (SalaryMin/Max/Currency/Period). Everything else spot-checked accurate. |
PRODUCT_RESEARCH.md |
Excellent — restore as docs/research/competitors.md |
Feature matrix stale on the same 2 points (match-score, salary). Covers the tracker market only — no Novoresume/Reactive Resume/ElegantCV. |
SECURITY_REPORT.md + M013–M015 |
Restore as docs/security/assessments/ |
Findings verified fixed; DP-key rotation still open. |
Keep archived: s06-acceptance-run.md, s07-uat.md, jobbjakt-next-session.md, jobbjakt-cleanup-tracker.md, MERGE_REQUEST.md, gmail-correspondence-phase1.md — point-in-time session artifacts, correctly archived.
Delete: the 0-byte files (decisions/ADR-00{1,2,3,4}, 09-research/*, 10-development/known-issues.md). They imply content that has never existed.
Historical decisions worth preserving (recovered from archive + code, currently recorded nowhere active):
JobTrackerBackendlink-compilation exists so tests can reach controllers without the web host — deliberate, self-described "transitional".Statusis free-text and canonicalized in the application layer specifically so custom user values are never destroyed (JobPipeline.csdocstring). Good decision; undocumented outside the source file.- The CI test whitelist was removed after it "silently skipped new suites and let two regressions reach main". Recorded only as a code comment in the workflow.
- Ollama is intentionally not bundled by default (
bundled-ollamaprofile) so deploys reuse a shared instance. AI_PROVIDER=geminiexists specifically "to offload a weak local GPU" in prod (compose comment).
14. Recommended roadmap
Full task breakdown in docs/implementation-roadmap.md. Ordering rationale:
- Phase 1 — Critical fixes. The Job/Application split gates the guide's #2 priority workflow; nothing downstream is worth building first. The unauthenticated AI sidecar is a live security exposure. Documentation restoration is nearly free and prevents every future session from building on fiction.
- Phase 2 — UX. Onboarding + the Profile/Career split. Cheap, high-visibility.
- Phase 3 — Career Workspace. Requires the profile model to be real.
- Phase 4 — CV Builder. Requires Phase 3 + a data-driven theme system. The largest single piece of work in the plan.
- Phase 5 — AI. Mostly polish; the hard part is done.
- Phase 6 — Job Discovery. Greenfield; explicitly an enhancement.
- Phase 7 — SaaS. Last, per the guide's "do not over-engineer before needed".
15. Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Job/Application split touches everything | High | High | 38 endpoints, the Kanban, the table, the rules engine, and the wizard all assume one entity. Do it behind a migration + additive stage first (add Saved/Preparing to JobPipeline), then split the entity. Do not big-bang it. |
| CV Builder is scoped as a "tab" but is a subsystem | High | High | Theme model + content editor + live preview + customise panel ≈ the largest item in the plan. The inert tab makes it look nearly done. It is not started. |
| Unauthenticated AI sidecar exploited / API key drained | Medium | High | Remove host port mapping + add shared secret. Cheap; do it in Phase 1. |
| DP keys recoverable from git history | Medium | Medium | Rotate. Open since 2026-07-03. |
| Docs mislead the next session into rebuilding what exists | Certain | High | The add-job wizard, match scoring, and interview-prep already exist but read as "missing" from the stub docs. Restore SYSTEM_OVERVIEW.md first. |
| Norway-only import plugins vs SaaS ambition | Medium | Medium | Finn/NAV/Jobbnorge are NO-specific. International SaaS needs a plugin strategy or extension-based capture. Unstated product decision. |
| Frontend rewrite temptation | Medium | High | Three stacks invite a "clean rewrite". The guide forbids it. Consolidate incrementally: retire react-scripts as test runner, pick one router. |
| Unbounded AI/storage cost under multi-user | Medium | High | No quota exists. Must land before opening registration. |
| Mockups unverified | Medium | Medium | F:\Pictures\website\jobtracker\new referenced by prior sessions but not read here. Confirm before any redesign. |
| God controllers slow every change | High | Medium | Split opportunistically while doing Phase 1, not as a standalone refactor. |
Appendix — verification method
Known method gap (found 2026-07-17): this audit searched the working tree,
main, anddocs/_archive/. It never rangit branch -a. That missedfeature/career-workspace— 10 unmerged commits containing the CV-builder competitor research, a Career Workspace product strategy, an F0–F5 roadmap, and working code (CareerProfileService,CvVariant/CvVersion/TailoredApplication, interview-prep persistence) with tests. Two conclusions in this report (§8 "not analysed in this repo", and the §5.3 "planned, zero code" rows for CV variants) are wrong as a result; both are corrected in place. Any future audit of this repo must enumerate branches before concluding a feature does not exist.
Every other claim traces to a read of the code. Key verifications:
careerViewdead:grep -rn "careerView" job-tracker-ui/src/→ 3 hits (1 call site, 2 declarations), 0 reads.- Pipeline stages:
Services/JobPipeline.cs:24-31. - No job discovery:
grep -rln "JobSearch\|discovery" JobTrackerApi/ --include=*.cs→ 0. - No SaaS:
grep -rlin "stripe\|subscription\|billing\|quota\|PlanTier" JobTrackerApi/ Models/ Data/→ 0. - No public CV:
grep -rn "IsPublic\|PublicSlug\|public-cv" Controllers/ Models/→ 0. - No CAPTCHA:
grep -rli "captcha" JobTrackerApi/ --include=*.cs→ 0. - Sidecar auth:
grep -n "Depends\|api_key\|Authorization" tools/summarizer/app.py→ only outbound Groq header. - DP keys in history:
git log --oneline --all -- JobTrackerApi/keys→519c32e,955cae6. - Registration default:
Controllers/AuthController.cs:135—_cfg.GetValue("Auth:AllowRegistration", false). - Doc sizes:
wc -cacrossdocs/**/*.md.
Not verified (out of read-only scope): mockups on F:\, dependency CVEs, production runtime configuration.