From b176a44627734b641c996db60b76968bffa0007d Mon Sep 17 00:00:00 2001 From: cesnimda Date: Fri, 17 Jul 2026 17:04:32 +0200 Subject: [PATCH] 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 --- docs/00-ai-context.md | 732 ++++++++++++++++++ docs/01-glossary.md | 270 +++++++ docs/02-product-principles.md | 105 +++ docs/03-design-principles.md | 113 +++ docs/AI_SESSION_START.md | 90 +++ docs/CHANGELOG_AI_WORK.md | 212 +++++ docs/MASTER_IMPLEMENTATION_GUIDE.md | 707 +++++++++++++++++ docs/_archive/01-product/business-model.md | 0 docs/_archive/01-product/personas.md | 0 docs/_archive/01-product/product-vision.md | 0 docs/_archive/01-product/roadmap.md | 0 .../02-user-experience/design-principles.md | 0 .../_archive/02-user-experience/navigation.md | 0 .../02-user-experience/onboarding-flow.md | 0 .../02-user-experience/user-journeys.md | 0 .../_archive/03-core-features/applications.md | 0 .../03-core-features/communications.md | 0 docs/_archive/03-core-features/companies.md | 0 docs/_archive/03-core-features/follow-ups.md | 0 .../03-core-features/global-search.md | 0 .../_archive/03-core-features/job-tracking.md | 0 .../04-career-workspace/career-profile.md | 0 .../cover-letter-builder.md | 0 .../04-career-workspace/cv-builder.md | 0 .../04-career-workspace/cv-rendering.md | 0 .../_archive/04-career-workspace/cv-themes.md | 0 .../_archive/04-career-workspace/portfolio.md | 0 docs/_archive/05-ai/ai-architecture.md | 0 docs/_archive/05-ai/ai-features.md | 0 docs/_archive/05-ai/ai-providers.md | 0 docs/_archive/05-ai/ai-safety.md | 0 .../06-integrations/authentication.md | 0 docs/_archive/06-integrations/calendar.md | 0 .../06-integrations/email-providers.md | 0 docs/_archive/06-integrations/job-search.md | 0 docs/_archive/07-security/file-security.md | 0 docs/_archive/07-security/privacy.md | 0 docs/_archive/07-security/saas-readiness.md | 0 .../07-security/security-requirements.md | 0 .../08-architecture/current-architecture.md | 0 .../08-architecture/database-model.md | 0 .../08-architecture/future-architecture.md | 0 .../08-architecture/migration-plan.md | 0 .../10-development/coding-guidelines.md | 0 docs/_archive/10-development/deployment.md | 0 .../10-development/testing-strategy.md | 0 docs/{ => _archive}/ARCHITECTURE_EVOLUTION.md | 0 docs/{ => _archive}/MERGE_REQUEST.md | 0 docs/{ => _archive}/PRODUCT_EVOLUTION_PLAN.md | 0 docs/{ => _archive}/PRODUCT_RESEARCH.md | 0 docs/{ => _archive}/ROADMAP.md | 0 docs/{ => _archive}/SECURITY_REPORT.md | 0 docs/{ => _archive}/SYSTEM_OVERVIEW.md | 0 .../s06-acceptance/.dev-auth-token.txt | 0 docs/_archive/claude.md | 20 + .../cv-builder-parser-benchmark.md | 0 docs/_archive/docs/00-ai-context.md | 732 ++++++++++++++++++ docs/_archive/docs/01-glossary.md | 270 +++++++ docs/_archive/docs/02-product-principles.md | 105 +++ docs/_archive/docs/03-design-principles.md | 113 +++ docs/_archive/docs/ai/overview.md | 3 + docs/_archive/docs/ai/prompts.md | 3 + docs/_archive/docs/ai/providers.md | 3 + docs/_archive/docs/ai/safety.md | 3 + docs/_archive/docs/architecture/backend.md | 3 + docs/_archive/docs/architecture/current.md | 3 + docs/_archive/docs/architecture/database.md | 3 + docs/_archive/docs/architecture/deployment.md | 3 + docs/_archive/docs/architecture/frontend.md | 3 + docs/_archive/docs/architecture/future.md | 3 + docs/_archive/docs/auth/2fa.md | 3 + docs/_archive/docs/auth/login.md | 3 + docs/_archive/docs/auth/oauth.md | 3 + docs/_archive/docs/auth/overview.md | 3 + docs/_archive/docs/auth/signup.md | 3 + docs/_archive/docs/career/education.md | 3 + docs/_archive/docs/career/experience.md | 3 + docs/_archive/docs/career/master-profile.md | 3 + docs/_archive/docs/career/overview.md | 3 + docs/_archive/docs/career/projects.md | 3 + docs/_archive/docs/career/skills.md | 3 + docs/_archive/docs/cover-letters/ai.md | 3 + docs/_archive/docs/cover-letters/builder.md | 3 + docs/_archive/docs/cover-letters/overview.md | 3 + docs/_archive/docs/cv-builder/ai.md | 3 + docs/_archive/docs/cv-builder/content.md | 3 + .../_archive/docs/cv-builder/customisation.md | 3 + docs/_archive/docs/cv-builder/exports.md | 3 + docs/_archive/docs/cv-builder/overview.md | 3 + docs/_archive/docs/cv-builder/rendering.md | 3 + docs/_archive/docs/cv-builder/themes.md | 3 + docs/_archive/docs/cv-builder/workflow.md | 3 + docs/_archive/docs/documentation-rules.md | 3 + docs/_archive/docs/jobs/applications.md | 3 + docs/_archive/docs/jobs/companies.md | 3 + docs/_archive/docs/jobs/followups.md | 3 + docs/_archive/docs/jobs/job-import.md | 3 + docs/_archive/docs/jobs/job-search.md | 3 + docs/_archive/docs/jobs/overview.md | 3 + docs/_archive/docs/product/business-model.md | 3 + docs/_archive/docs/product/goals.md | 3 + docs/_archive/docs/product/mission.md | 3 + docs/_archive/docs/product/non-goals.md | 3 + docs/_archive/docs/product/personas.md | 3 + docs/_archive/docs/product/roadmap.md | 3 + docs/_archive/docs/product/vision.md | 3 + docs/_archive/docs/research/flowcv.md | 3 + docs/_archive/docs/research/jobscan.md | 3 + docs/_archive/docs/research/novoresume.md | 3 + .../_archive/docs/research/reactive-resume.md | 3 + docs/_archive/docs/security/overview.md | 3 + docs/_archive/docs/security/privacy.md | 3 + docs/_archive/docs/security/uploads.md | 3 + docs/_archive/docs/security/validation.md | 3 + docs/_archive/docs/ux/accessibility.md | 3 + docs/_archive/docs/ux/dashboard.md | 3 + docs/_archive/docs/ux/design-system.md | 3 + docs/_archive/docs/ux/navigation.md | 3 + docs/_archive/docs/ux/onboarding.md | 3 + docs/_archive/docs/ux/profile.md | 3 + docs/_archive/docs/ux/settings.md | 3 + docs/_archive/documentation-rules.md | 171 ++++ .../gmail-correspondence-phase1.md | 0 docs/_archive/infrastructure-investigation.md | 581 ++++++++++++++ .../jobbjakt-cleanup-tracker.md | 0 docs/{ => _archive}/jobbjakt-next-session.md | 0 .../performance/MEMORY_LEAK_REPORT.md | 0 .../performance/PERFORMANCE_IMPROVEMENTS.md | 0 .../performance/ROOT_CAUSE_ANALYSIS.md | 0 docs/{ => _archive}/s06-acceptance-run.md | 0 docs/{ => _archive}/s07-uat.md | 0 .../M013-adversarial-security-assessment.md | 0 .../M014-security-remediation-verification.md | 0 .../M015-authorization-replay-report.md | 0 .../M015-hostile-fixture-setup.json | 0 .../M015-hostile-fixture-setup.md | 0 .../M015-s02-probe-results.json | 0 docs/ai/ai-principles.md | 55 ++ docs/ai/cost-control.md | 39 + docs/ai/cover-letter-ai.md | 49 ++ docs/ai/cv-ai.md | 58 ++ docs/ai/future-ai.md | 49 ++ docs/ai/jobs-ai.md | 48 ++ docs/ai/model-selection.md | 60 ++ docs/ai/overview.md | 72 ++ docs/ai/privacy.md | 37 + docs/ai/prompts.md | 51 ++ docs/ai/providers.md | 66 ++ docs/ai/safety.md | 3 + docs/ai/security.md | 44 ++ docs/application-discovery-report.md | 638 +++++++++++++++ docs/architecture/ai-architecture.md | 56 ++ docs/architecture/api-design.md | 39 + docs/architecture/authentication.md | 48 ++ docs/architecture/backend.md | 75 ++ docs/architecture/background-jobs.md | 36 + docs/architecture/career-architecture.md | 55 ++ docs/architecture/current.md | 375 +++++++++ docs/architecture/cv-rendering.md | 56 ++ docs/architecture/database.md | 78 ++ docs/architecture/deployment.md | 3 + docs/architecture/file-storage.md | 36 + docs/architecture/frontend.md | 64 ++ docs/architecture/future.md | 3 + docs/architecture/migrations.md | 35 + docs/architecture/overview.md | 93 +++ docs/architecture/principles.md | 46 ++ docs/architecture/technical-debt.md | 56 ++ docs/auth/2fa.md | 3 + docs/auth/login.md | 3 + docs/auth/oauth.md | 3 + docs/auth/overview.md | 3 + docs/auth/signup.md | 3 + docs/career-workspace-branch-assessment.md | 158 ++++ docs/career/career-profile.md | 94 +++ docs/career/cover-letters.md | 39 + docs/career/cv-ai.md | 46 ++ docs/career/cv-builder | 0 docs/career/cv-rendering.md | 68 ++ docs/career/cv-tailoring.md | 53 ++ docs/career/cv-themes.md | 78 ++ docs/career/documents.md | 34 + docs/career/education.md | 3 + docs/career/experience.md | 3 + docs/career/future-features.md | 48 ++ docs/career/master-profile.md | 52 ++ docs/career/overview.md | 87 +++ docs/career/portfolio.md | 25 + docs/career/projects.md | 3 + docs/career/public-profile.md | 46 ++ docs/career/skills.md | 3 + docs/career/versioning.md | 35 + docs/cover-letters/ai.md | 3 + docs/cover-letters/builder.md | 3 + docs/cover-letters/overview.md | 3 + docs/cv-builder/ai.md | 3 + docs/cv-builder/content.md | 3 + docs/cv-builder/customisation.md | 3 + docs/cv-builder/exports.md | 3 + docs/cv-builder/overview.md | 3 + docs/cv-builder/rendering.md | 3 + docs/cv-builder/themes.md | 3 + docs/cv-builder/workflow.md | 3 + .../ADR-002-job-application-model.md | 109 +++ docs/documentation-rules.md | 3 + docs/implementation-roadmap.md | 227 ++++++ docs/jobs/adding-jobs.md | 74 ++ docs/jobs/analytics.md | 29 + docs/jobs/applications.md | 3 + docs/jobs/communication-tracking.md | 49 ++ docs/jobs/company-management.md | 58 ++ docs/jobs/followups.md | 3 + docs/jobs/future-features.md | 51 ++ docs/jobs/job-import.md | 50 ++ docs/jobs/job-lifecycle.md | 91 +++ docs/jobs/job-search.md | 55 ++ docs/jobs/overview.md | 141 ++++ docs/phase-0-foundation-report.md | 253 ++++++ docs/product/business-model.md | 3 + docs/product/goals.md | 111 +++ docs/product/mission.md | 76 ++ docs/product/non-goals.md | 66 ++ docs/product/personas.md | 121 +++ docs/product/roadmap.md | 329 ++++++++ docs/product/vision.md | 149 ++++ docs/research/competitors.md | 134 ++++ docs/research/cv-builder-patterns.md | 52 ++ docs/research/elegantcv-analysis.md | 45 ++ docs/research/feature-comparison.md | 32 + docs/research/flowcv-analysis.md | 89 +++ docs/research/job-platform-analysis.md | 54 ++ docs/research/jobscan.md | 3 + docs/research/market-positioning.md | 53 ++ docs/research/novoresume-analysis.md | 57 ++ docs/research/overview.md | 74 ++ docs/research/reactive-resume-analysis.md | 50 ++ docs/research/recommendations.md | 61 ++ docs/research/saas-analysis.md | 36 + docs/security/ai-security.md | 35 + docs/security/api-security.md | 40 + docs/security/authentication.md | 84 ++ docs/security/authorization.md | 43 + docs/security/file-security.md | 56 ++ docs/security/input-validation.md | 48 ++ docs/security/overview.md | 64 ++ docs/security/privacy.md | 51 ++ docs/security/saas-security.md | 51 ++ docs/security/two-factor-authentication.md | 82 ++ docs/security/uploads.md | 3 + docs/security/user-data.md | 48 ++ docs/security/validation.md | 3 + docs/technical/coding-standards.md | 52 ++ docs/technical/deployment.md | 41 + docs/technical/docker.md | 51 ++ docs/technical/environment.md | 32 + docs/technical/git-workflow.md | 53 ++ docs/technical/logging.md | 45 ++ docs/technical/monitoring.md | 39 + docs/technical/overview.md | 41 + docs/technical/performance.md | 36 + docs/technical/testing.md | 65 ++ docs/technical/troubleshooting.md | 32 + docs/ux/accessibility.md | 47 ++ docs/ux/dashboard.md | 73 ++ docs/ux/design-system.md | 74 ++ docs/ux/empty-states.md | 30 + docs/ux/errors.md | 29 + docs/ux/loading-states.md | 29 + docs/ux/navigation.md | 121 +++ docs/ux/notifications.md | 41 + docs/ux/onboarding.md | 181 +++++ docs/ux/profile.md | 59 ++ docs/ux/responsive.md | 47 ++ docs/ux/settings.md | 88 +++ docs/ux/user-flows.md | 99 +++ 275 files changed, 12554 insertions(+) create mode 100644 docs/00-ai-context.md create mode 100644 docs/01-glossary.md create mode 100644 docs/02-product-principles.md create mode 100644 docs/03-design-principles.md create mode 100644 docs/AI_SESSION_START.md create mode 100644 docs/CHANGELOG_AI_WORK.md create mode 100644 docs/MASTER_IMPLEMENTATION_GUIDE.md create mode 100644 docs/_archive/01-product/business-model.md create mode 100644 docs/_archive/01-product/personas.md create mode 100644 docs/_archive/01-product/product-vision.md create mode 100644 docs/_archive/01-product/roadmap.md create mode 100644 docs/_archive/02-user-experience/design-principles.md create mode 100644 docs/_archive/02-user-experience/navigation.md create mode 100644 docs/_archive/02-user-experience/onboarding-flow.md create mode 100644 docs/_archive/02-user-experience/user-journeys.md create mode 100644 docs/_archive/03-core-features/applications.md create mode 100644 docs/_archive/03-core-features/communications.md create mode 100644 docs/_archive/03-core-features/companies.md create mode 100644 docs/_archive/03-core-features/follow-ups.md create mode 100644 docs/_archive/03-core-features/global-search.md create mode 100644 docs/_archive/03-core-features/job-tracking.md create mode 100644 docs/_archive/04-career-workspace/career-profile.md create mode 100644 docs/_archive/04-career-workspace/cover-letter-builder.md create mode 100644 docs/_archive/04-career-workspace/cv-builder.md create mode 100644 docs/_archive/04-career-workspace/cv-rendering.md create mode 100644 docs/_archive/04-career-workspace/cv-themes.md create mode 100644 docs/_archive/04-career-workspace/portfolio.md create mode 100644 docs/_archive/05-ai/ai-architecture.md create mode 100644 docs/_archive/05-ai/ai-features.md create mode 100644 docs/_archive/05-ai/ai-providers.md create mode 100644 docs/_archive/05-ai/ai-safety.md create mode 100644 docs/_archive/06-integrations/authentication.md create mode 100644 docs/_archive/06-integrations/calendar.md create mode 100644 docs/_archive/06-integrations/email-providers.md create mode 100644 docs/_archive/06-integrations/job-search.md create mode 100644 docs/_archive/07-security/file-security.md create mode 100644 docs/_archive/07-security/privacy.md create mode 100644 docs/_archive/07-security/saas-readiness.md create mode 100644 docs/_archive/07-security/security-requirements.md create mode 100644 docs/_archive/08-architecture/current-architecture.md create mode 100644 docs/_archive/08-architecture/database-model.md create mode 100644 docs/_archive/08-architecture/future-architecture.md create mode 100644 docs/_archive/08-architecture/migration-plan.md create mode 100644 docs/_archive/10-development/coding-guidelines.md create mode 100644 docs/_archive/10-development/deployment.md create mode 100644 docs/_archive/10-development/testing-strategy.md rename docs/{ => _archive}/ARCHITECTURE_EVOLUTION.md (100%) rename docs/{ => _archive}/MERGE_REQUEST.md (100%) rename docs/{ => _archive}/PRODUCT_EVOLUTION_PLAN.md (100%) rename docs/{ => _archive}/PRODUCT_RESEARCH.md (100%) rename docs/{ => _archive}/ROADMAP.md (100%) rename docs/{ => _archive}/SECURITY_REPORT.md (100%) rename docs/{ => _archive}/SYSTEM_OVERVIEW.md (100%) rename docs/{ => _archive}/artifacts/s06-acceptance/.dev-auth-token.txt (100%) create mode 100644 docs/_archive/claude.md rename docs/{ => _archive}/cv-builder-parser-benchmark.md (100%) create mode 100644 docs/_archive/docs/00-ai-context.md create mode 100644 docs/_archive/docs/01-glossary.md create mode 100644 docs/_archive/docs/02-product-principles.md create mode 100644 docs/_archive/docs/03-design-principles.md create mode 100644 docs/_archive/docs/ai/overview.md create mode 100644 docs/_archive/docs/ai/prompts.md create mode 100644 docs/_archive/docs/ai/providers.md create mode 100644 docs/_archive/docs/ai/safety.md create mode 100644 docs/_archive/docs/architecture/backend.md create mode 100644 docs/_archive/docs/architecture/current.md create mode 100644 docs/_archive/docs/architecture/database.md create mode 100644 docs/_archive/docs/architecture/deployment.md create mode 100644 docs/_archive/docs/architecture/frontend.md create mode 100644 docs/_archive/docs/architecture/future.md create mode 100644 docs/_archive/docs/auth/2fa.md create mode 100644 docs/_archive/docs/auth/login.md create mode 100644 docs/_archive/docs/auth/oauth.md create mode 100644 docs/_archive/docs/auth/overview.md create mode 100644 docs/_archive/docs/auth/signup.md create mode 100644 docs/_archive/docs/career/education.md create mode 100644 docs/_archive/docs/career/experience.md create mode 100644 docs/_archive/docs/career/master-profile.md create mode 100644 docs/_archive/docs/career/overview.md create mode 100644 docs/_archive/docs/career/projects.md create mode 100644 docs/_archive/docs/career/skills.md create mode 100644 docs/_archive/docs/cover-letters/ai.md create mode 100644 docs/_archive/docs/cover-letters/builder.md create mode 100644 docs/_archive/docs/cover-letters/overview.md create mode 100644 docs/_archive/docs/cv-builder/ai.md create mode 100644 docs/_archive/docs/cv-builder/content.md create mode 100644 docs/_archive/docs/cv-builder/customisation.md create mode 100644 docs/_archive/docs/cv-builder/exports.md create mode 100644 docs/_archive/docs/cv-builder/overview.md create mode 100644 docs/_archive/docs/cv-builder/rendering.md create mode 100644 docs/_archive/docs/cv-builder/themes.md create mode 100644 docs/_archive/docs/cv-builder/workflow.md create mode 100644 docs/_archive/docs/documentation-rules.md create mode 100644 docs/_archive/docs/jobs/applications.md create mode 100644 docs/_archive/docs/jobs/companies.md create mode 100644 docs/_archive/docs/jobs/followups.md create mode 100644 docs/_archive/docs/jobs/job-import.md create mode 100644 docs/_archive/docs/jobs/job-search.md create mode 100644 docs/_archive/docs/jobs/overview.md create mode 100644 docs/_archive/docs/product/business-model.md create mode 100644 docs/_archive/docs/product/goals.md create mode 100644 docs/_archive/docs/product/mission.md create mode 100644 docs/_archive/docs/product/non-goals.md create mode 100644 docs/_archive/docs/product/personas.md create mode 100644 docs/_archive/docs/product/roadmap.md create mode 100644 docs/_archive/docs/product/vision.md create mode 100644 docs/_archive/docs/research/flowcv.md create mode 100644 docs/_archive/docs/research/jobscan.md create mode 100644 docs/_archive/docs/research/novoresume.md create mode 100644 docs/_archive/docs/research/reactive-resume.md create mode 100644 docs/_archive/docs/security/overview.md create mode 100644 docs/_archive/docs/security/privacy.md create mode 100644 docs/_archive/docs/security/uploads.md create mode 100644 docs/_archive/docs/security/validation.md create mode 100644 docs/_archive/docs/ux/accessibility.md create mode 100644 docs/_archive/docs/ux/dashboard.md create mode 100644 docs/_archive/docs/ux/design-system.md create mode 100644 docs/_archive/docs/ux/navigation.md create mode 100644 docs/_archive/docs/ux/onboarding.md create mode 100644 docs/_archive/docs/ux/profile.md create mode 100644 docs/_archive/docs/ux/settings.md create mode 100644 docs/_archive/documentation-rules.md rename docs/{ => _archive}/gmail-correspondence-phase1.md (100%) create mode 100644 docs/_archive/infrastructure-investigation.md rename docs/{ => _archive}/jobbjakt-cleanup-tracker.md (100%) rename docs/{ => _archive}/jobbjakt-next-session.md (100%) rename docs/{ => _archive}/performance/MEMORY_LEAK_REPORT.md (100%) rename docs/{ => _archive}/performance/PERFORMANCE_IMPROVEMENTS.md (100%) rename docs/{ => _archive}/performance/ROOT_CAUSE_ANALYSIS.md (100%) rename docs/{ => _archive}/s06-acceptance-run.md (100%) rename docs/{ => _archive}/s07-uat.md (100%) rename docs/{ => _archive}/security-assessments/M013-adversarial-security-assessment.md (100%) rename docs/{ => _archive}/security-assessments/M014-security-remediation-verification.md (100%) rename docs/{ => _archive}/security-assessments/M015-authorization-replay-report.md (100%) rename docs/{ => _archive}/security-assessments/M015-hostile-fixture-setup.json (100%) rename docs/{ => _archive}/security-assessments/M015-hostile-fixture-setup.md (100%) rename docs/{ => _archive}/security-assessments/M015-s02-probe-results.json (100%) create mode 100644 docs/ai/ai-principles.md create mode 100644 docs/ai/cost-control.md create mode 100644 docs/ai/cover-letter-ai.md create mode 100644 docs/ai/cv-ai.md create mode 100644 docs/ai/future-ai.md create mode 100644 docs/ai/jobs-ai.md create mode 100644 docs/ai/model-selection.md create mode 100644 docs/ai/overview.md create mode 100644 docs/ai/privacy.md create mode 100644 docs/ai/prompts.md create mode 100644 docs/ai/providers.md create mode 100644 docs/ai/safety.md create mode 100644 docs/ai/security.md create mode 100644 docs/application-discovery-report.md create mode 100644 docs/architecture/ai-architecture.md create mode 100644 docs/architecture/api-design.md create mode 100644 docs/architecture/authentication.md create mode 100644 docs/architecture/backend.md create mode 100644 docs/architecture/background-jobs.md create mode 100644 docs/architecture/career-architecture.md create mode 100644 docs/architecture/current.md create mode 100644 docs/architecture/cv-rendering.md create mode 100644 docs/architecture/database.md create mode 100644 docs/architecture/deployment.md create mode 100644 docs/architecture/file-storage.md create mode 100644 docs/architecture/frontend.md create mode 100644 docs/architecture/future.md create mode 100644 docs/architecture/migrations.md create mode 100644 docs/architecture/overview.md create mode 100644 docs/architecture/principles.md create mode 100644 docs/architecture/technical-debt.md create mode 100644 docs/auth/2fa.md create mode 100644 docs/auth/login.md create mode 100644 docs/auth/oauth.md create mode 100644 docs/auth/overview.md create mode 100644 docs/auth/signup.md create mode 100644 docs/career-workspace-branch-assessment.md create mode 100644 docs/career/career-profile.md create mode 100644 docs/career/cover-letters.md create mode 100644 docs/career/cv-ai.md create mode 100644 docs/career/cv-builder create mode 100644 docs/career/cv-rendering.md create mode 100644 docs/career/cv-tailoring.md create mode 100644 docs/career/cv-themes.md create mode 100644 docs/career/documents.md create mode 100644 docs/career/education.md create mode 100644 docs/career/experience.md create mode 100644 docs/career/future-features.md create mode 100644 docs/career/master-profile.md create mode 100644 docs/career/overview.md create mode 100644 docs/career/portfolio.md create mode 100644 docs/career/projects.md create mode 100644 docs/career/public-profile.md create mode 100644 docs/career/skills.md create mode 100644 docs/career/versioning.md create mode 100644 docs/cover-letters/ai.md create mode 100644 docs/cover-letters/builder.md create mode 100644 docs/cover-letters/overview.md create mode 100644 docs/cv-builder/ai.md create mode 100644 docs/cv-builder/content.md create mode 100644 docs/cv-builder/customisation.md create mode 100644 docs/cv-builder/exports.md create mode 100644 docs/cv-builder/overview.md create mode 100644 docs/cv-builder/rendering.md create mode 100644 docs/cv-builder/themes.md create mode 100644 docs/cv-builder/workflow.md create mode 100644 docs/decisions/ADR-002-job-application-model.md create mode 100644 docs/documentation-rules.md create mode 100644 docs/implementation-roadmap.md create mode 100644 docs/jobs/adding-jobs.md create mode 100644 docs/jobs/analytics.md create mode 100644 docs/jobs/applications.md create mode 100644 docs/jobs/communication-tracking.md create mode 100644 docs/jobs/company-management.md create mode 100644 docs/jobs/followups.md create mode 100644 docs/jobs/future-features.md create mode 100644 docs/jobs/job-import.md create mode 100644 docs/jobs/job-lifecycle.md create mode 100644 docs/jobs/job-search.md create mode 100644 docs/jobs/overview.md create mode 100644 docs/phase-0-foundation-report.md create mode 100644 docs/product/business-model.md create mode 100644 docs/product/goals.md create mode 100644 docs/product/mission.md create mode 100644 docs/product/non-goals.md create mode 100644 docs/product/personas.md create mode 100644 docs/product/roadmap.md create mode 100644 docs/product/vision.md create mode 100644 docs/research/competitors.md create mode 100644 docs/research/cv-builder-patterns.md create mode 100644 docs/research/elegantcv-analysis.md create mode 100644 docs/research/feature-comparison.md create mode 100644 docs/research/flowcv-analysis.md create mode 100644 docs/research/job-platform-analysis.md create mode 100644 docs/research/jobscan.md create mode 100644 docs/research/market-positioning.md create mode 100644 docs/research/novoresume-analysis.md create mode 100644 docs/research/overview.md create mode 100644 docs/research/reactive-resume-analysis.md create mode 100644 docs/research/recommendations.md create mode 100644 docs/research/saas-analysis.md create mode 100644 docs/security/ai-security.md create mode 100644 docs/security/api-security.md create mode 100644 docs/security/authentication.md create mode 100644 docs/security/authorization.md create mode 100644 docs/security/file-security.md create mode 100644 docs/security/input-validation.md create mode 100644 docs/security/overview.md create mode 100644 docs/security/privacy.md create mode 100644 docs/security/saas-security.md create mode 100644 docs/security/two-factor-authentication.md create mode 100644 docs/security/uploads.md create mode 100644 docs/security/user-data.md create mode 100644 docs/security/validation.md create mode 100644 docs/technical/coding-standards.md create mode 100644 docs/technical/deployment.md create mode 100644 docs/technical/docker.md create mode 100644 docs/technical/environment.md create mode 100644 docs/technical/git-workflow.md create mode 100644 docs/technical/logging.md create mode 100644 docs/technical/monitoring.md create mode 100644 docs/technical/overview.md create mode 100644 docs/technical/performance.md create mode 100644 docs/technical/testing.md create mode 100644 docs/technical/troubleshooting.md create mode 100644 docs/ux/accessibility.md create mode 100644 docs/ux/dashboard.md create mode 100644 docs/ux/design-system.md create mode 100644 docs/ux/empty-states.md create mode 100644 docs/ux/errors.md create mode 100644 docs/ux/loading-states.md create mode 100644 docs/ux/navigation.md create mode 100644 docs/ux/notifications.md create mode 100644 docs/ux/onboarding.md create mode 100644 docs/ux/profile.md create mode 100644 docs/ux/responsive.md create mode 100644 docs/ux/settings.md create mode 100644 docs/ux/user-flows.md diff --git a/docs/00-ai-context.md b/docs/00-ai-context.md new file mode 100644 index 0000000..3772a94 --- /dev/null +++ b/docs/00-ai-context.md @@ -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. \ No newline at end of file diff --git a/docs/01-glossary.md b/docs/01-glossary.md new file mode 100644 index 0000000..d571841 --- /dev/null +++ b/docs/01-glossary.md @@ -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. \ No newline at end of file diff --git a/docs/02-product-principles.md b/docs/02-product-principles.md new file mode 100644 index 0000000..b1cb49f --- /dev/null +++ b/docs/02-product-principles.md @@ -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. diff --git a/docs/03-design-principles.md b/docs/03-design-principles.md new file mode 100644 index 0000000..93c8bca --- /dev/null +++ b/docs/03-design-principles.md @@ -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. diff --git a/docs/AI_SESSION_START.md b/docs/AI_SESSION_START.md new file mode 100644 index 0000000..e9d0b07 --- /dev/null +++ b/docs/AI_SESSION_START.md @@ -0,0 +1,90 @@ +# AI Session Start Instructions + +Before making changes: + +1. Read: + +docs/MASTER_IMPLEMENTATION_GUIDE.md + +2. Read relevant documentation: + +docs/product/ +docs/architecture/ +docs/security/ +docs/technical/ + + +3. Review: + +- Current git status. +- Recent changes. +- Existing implementation. + +--- + +# Working Rules + +You are working on an existing production application. + +Do not: + +- Rewrite without justification. +- Remove working features. +- Change architecture without understanding impact. +- Assume documentation is correct over code. + +--- + +# Before Coding + +Explain: + +- What you found. +- What needs changing. +- Why. + +For larger changes: + +Create an implementation plan. + +--- + +# During Coding + +Prefer: + +- Small commits. +- Clean abstractions. +- Tests. +- Documentation updates. + +--- + +# Continue Working + +Do not stop after every small task. + +Continue with related improvements. + +Only ask for input when: + +- A decision cannot reasonably be inferred. +- Multiple options have significant consequences. +- Credentials/access are required. +- A destructive choice is required. + +--- + +# After Work + +Update: + +docs/CHANGELOG_AI_WORK.md + +Include: + +- Summary. +- Files changed. +- Tests. +- Decisions. +- Next steps. \ No newline at end of file diff --git a/docs/CHANGELOG_AI_WORK.md b/docs/CHANGELOG_AI_WORK.md new file mode 100644 index 0000000..929034a --- /dev/null +++ b/docs/CHANGELOG_AI_WORK.md @@ -0,0 +1,212 @@ +# AI Development Changelog + +This document tracks work completed by AI coding assistants. + +Purpose: + +- Maintain project history. +- Record architectural decisions. +- Track incomplete work. +- Prevent repeated investigations. +- Help future AI sessions understand previous changes. + +--- + +# How To Use + +After making meaningful changes, update this file. + +Include: + +- Date. +- Task. +- Summary. +- Files changed. +- Decisions. +- Testing performed. +- Remaining work. + +--- + +# Current Status + +## Application State + +Status: + +Active development. + +The application is functional but undergoing major UX, architecture, and product improvements. + +--- + +# Completed Work + +## Authentication + +Status: + +In progress. + +Completed: + +- Existing authentication reviewed. +- OAuth architecture analysed. + +Remaining: + +- Full signup flow. +- Google login. +- Microsoft login. +- Password reset improvements. +- Two factor authentication. + +--- + +## Career Workspace + +Status: + +Planning phase. + +Completed: + +- Product direction defined. +- Architecture documented. +- Competitor research completed. + +Remaining: + +- Master career profile. +- CV builder redesign. +- Theme system. +- AI workflows. + +--- + +## Job Workflow + +Status: + +Core product. + +Completed: + +- Existing job tracking functionality. + +Remaining: + +- Improved add-job workflow. +- Better application pipeline. +- Better onboarding. + +--- + +# Current Development Session + +Date: + +YYYY-MM-DD + +## Objective + +Describe current goal. + +--- + +## Work Completed + +Example: + +- Updated authentication pages. +- Refactored settings layout. +- Added validation. + +--- + +## Files Changed + +Example: + +Backend: + +- path/file.cs + +Frontend: + +- path/file.tsx + +Documentation: + +- docs/file.md + +--- + +## Testing + +Completed: + +- Build successful. +- Tests passed. + +Not completed: + +- Manual testing required. +- External service testing required. + +--- + +# Architectural Decisions + +## Decision + +Date: + +Reason: + +Impact: + +--- + +# Known Issues + +## Issue + +Description: + +Possible causes: + +Next investigation: + +--- + +# Blocked Items + +## Item + +Reason blocked: + +Required input: + +--- + +# Recommended Next Tasks + +Priority order: + +1. + +2. + +3. + +--- + +# AI Notes + +Important context for future sessions: + +- Read MASTER_IMPLEMENTATION_GUIDE.md first. +- Do not redesign without reviewing existing architecture. +- Prefer incremental changes. +- Ask before destructive decisions. \ No newline at end of file diff --git a/docs/MASTER_IMPLEMENTATION_GUIDE.md b/docs/MASTER_IMPLEMENTATION_GUIDE.md new file mode 100644 index 0000000..8931e63 --- /dev/null +++ b/docs/MASTER_IMPLEMENTATION_GUIDE.md @@ -0,0 +1,707 @@ +# Jobjakt Master Implementation Guide + +Version: 1.0 + +--- + +# Purpose Of This Document + +This document is the primary source of truth for understanding and improving Jobjakt. + +Before making significant changes: + +1. Read this document. +2. Read the relevant linked documentation. +3. Understand existing architecture. +4. Make incremental improvements. + +Do not redesign based on assumptions. + +Always verify against the actual codebase. + +--- + +# Product Identity + +## Application Name + +Jobjakt + +--- + +# What Is Jobjakt? + +Jobjakt is a job tracking and career management application. + +The primary purpose is: + +> Help users organise, manage, and improve their job search. + +It is not primarily a CV builder. + +The CV system is a supporting feature. + +--- + +# Product Hierarchy + +The product hierarchy is: + + +CORE PRODUCT + +Job Tracking + +↓ + +Applications + +↓ + +Application Workflow + +↓ + +Follow-ups + +↓ + +Communication Tracking + +SUPPORTING FEATURES + +Career Profile + +↓ + +Master CV + +↓ + +CV Builder + +↓ + +Cover Letters + +↓ + +Portfolio + +↓ + +Interview Preparation + +ENHANCEMENT FEATURES + +Job Discovery + +↓ + +Find Opportunities + +↓ + +Import Into Tracker + + +--- + +# Core Principle + +Do not transform Jobjakt into a CV generator. + +The competitive advantage is: + +> Managing the entire journey from discovering a job to successfully applying. + +--- + +# Target Users + +Primary user: + +People actively looking for work. + +Examples: + +- Developers. +- Professionals changing careers. +- Graduates. +- Anyone applying for multiple jobs. + +--- + +# Current Product Goals + +Priority order: + +## 1. Excellent Job Tracking + +Users should easily: + +- Add jobs. +- Track status. +- Manage applications. +- Follow up. +- Store communication. + +--- + +## 2. Excellent Application Workflow + +A user should be guided through: + + +Find Job + +↓ + +Import Job + +↓ + +Review Details + +↓ + +Prepare CV + +↓ + +Prepare Cover Letter + +↓ + +Add Supporting Files + +↓ + +Submit Application + +↓ + +Track Progress + + +--- + +## 3. Career Workspace + +Users maintain: + +One professional identity. + +This includes: + +- Personal information. +- Work history. +- Education. +- Skills. +- Projects. +- Certifications. +- Languages. +- Achievements. + +This becomes the source of truth. + +--- + +# Career Architecture + +The relationship should be: + + +Career Profile + +↓ + +Master CV + +↓ + +CV Builder + +↓ + +Theme/Layout + +↓ + +Generated CV + + +--- + +For applications: + + +Career Profile + +↓ + +AI Analysis + +Job Description + +↓ + +Tailored CV Draft + +↓ + +User Review + +↓ + +Saved Copy Against Application + + +--- + +Important: + +The master CV must never be modified automatically. + +--- + +# CV Builder Vision + +The CV builder should become similar in quality to: + +- FlowCV. +- Novoresume. +- ElegantCV. + +Reference research: + +`docs/research/` + +--- + +# CV Builder Principles + +## Content And Design Are Separate + +Content: + +- Experience. +- Skills. +- Education. + +Design: + +- Theme. +- Colours. +- Fonts. +- Layout. + +--- + +Users should be able to: + +- Choose themes. +- Preview instantly. +- Change colours. +- Show/hide photo. +- Configure sections. +- Export. + +--- + +# Preferred Builder Experience + +Structure: + + +Content Tab + +↓ + +Customise Tab + +↓ + +Preview + +↓ + +Export + + +--- + +# Themes + +Initial goal: + +3-5 excellent themes. + +Categories: + +## ATS Professional + +Simple. + +Recruiter friendly. + + +## Modern Professional + +Clean SaaS style. + + +## Creative + +More visual. + +--- + +Themes should support: + +- Accent colours. +- Typography. +- Layout. +- Spacing. +- Photo options. +- Icons. + +--- + +# Authentication Requirements + +Users must support: + +## Email Signup + +User can: + +- Register. +- Login. +- Reset password. + +--- + +## OAuth + +Support: + +- Google. +- Microsoft. + +If OAuth user does not exist: + +Create account automatically. + +--- + +## Security + +Support: + +- Password security. +- Validation. +- Rate limiting. +- Two factor authentication. + +See: + +`docs/security/` + +--- + +# AI Philosophy + +AI assists users. + +AI does not replace users. + +--- + +AI can help with: + +- CV improvements. +- Job matching. +- Cover letters. +- Interview preparation. + +--- + +AI output must: + +- Be editable. +- Require approval. +- Never invent facts. + +--- + +See: + +`docs/ai/` + +--- + +# Job Search + +Job search is an enhancement. + +It should not replace job boards. + +--- + +Ideal workflow: + + +Search + +↓ + +Find Opportunity + +↓ + +Import Job + +↓ + +Track Application + + +--- + +Possible features: + +- Job title search. +- Location. +- Industry. +- Remote. +- Filters. +- Company information. + +Research required before implementation. + +--- + +# SaaS Direction + +Current: + +Personal application. + +Future: + +Commercial SaaS. + +--- + +Prepare for: + +- Multiple users. +- Premium features. +- AI limits. +- Storage limits. +- Premium themes. + +--- + +Do not over-engineer. + +Prioritise: + +Product quality first. + +--- + +# UI Direction + +The desired UI style: + +- Modern. +- Professional. +- Notion-inspired. +- Clean. +- Easy to navigate. +- Dark mode support. + +--- + +Avoid: + +- Clutter. +- Complex menus. +- Confusing workflows. + +--- + +# UX Principles + +The application should be: + +Simple enough for beginners. + +Powerful enough for serious users. + +--- + +Users should always understand: + +Where they are. + +What they can do. + +What happens next. + +--- + +# Development Rules + +When implementing: + +## Always + +- Review existing code first. +- Make incremental changes. +- Preserve working functionality. +- Add tests. +- Document decisions. + +--- + +## Avoid + +- Large rewrites. +- Breaking migrations. +- Duplicate systems. +- Unnecessary dependencies. + +--- + +# Code Quality + +Follow: + +`docs/technical/` + +--- + +# Architecture + +Follow: + +`docs/architecture/` + +--- + +# Security + +Follow: + +`docs/security/` + +--- + +# AI + +Follow: + +`docs/ai/` + +--- + +# Product Research + +Follow: + +`docs/research/` + +--- + +# Implementation Priority + +## Phase 1 + +Foundation: + +- Authentication. +- User experience. +- Navigation. +- Job workflow. + +--- + +## Phase 2 + +Career Workspace: + +- Master profile. +- CV management. +- Builder. + +--- + +## Phase 3 + +AI: + +- Tailoring. +- Assistance. +- Recommendations. + +--- + +## Phase 4 + +Advanced: + +- Job discovery. +- Public profiles. +- SaaS. + +--- + +# Working Style For AI Assistants + +When working on Jobjakt: + +Do not stop after every small task. + +Work autonomously. + +Continue improving areas that are safe. + +--- + +Only request user input when: + +- A product decision is required. +- Multiple valid directions exist. +- Credentials are needed. +- A destructive decision is required. + +--- + +If blocked: + +1. Document the blocker. +2. Find another useful task. +3. Continue working. + +--- + +# Final Goal + +Jobjakt should become: + +A premium career workspace. + +A user should maintain one professional identity and use Jobjakt to: + +- Find opportunities. +- Manage applications. +- Create documents. +- Improve applications. +- Track career progress. + +The CV is one output. + +The career journey is the product. \ No newline at end of file diff --git a/docs/_archive/01-product/business-model.md b/docs/_archive/01-product/business-model.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/01-product/personas.md b/docs/_archive/01-product/personas.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/01-product/product-vision.md b/docs/_archive/01-product/product-vision.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/01-product/roadmap.md b/docs/_archive/01-product/roadmap.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/02-user-experience/design-principles.md b/docs/_archive/02-user-experience/design-principles.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/02-user-experience/navigation.md b/docs/_archive/02-user-experience/navigation.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/02-user-experience/onboarding-flow.md b/docs/_archive/02-user-experience/onboarding-flow.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/02-user-experience/user-journeys.md b/docs/_archive/02-user-experience/user-journeys.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/03-core-features/applications.md b/docs/_archive/03-core-features/applications.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/03-core-features/communications.md b/docs/_archive/03-core-features/communications.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/03-core-features/companies.md b/docs/_archive/03-core-features/companies.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/03-core-features/follow-ups.md b/docs/_archive/03-core-features/follow-ups.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/03-core-features/global-search.md b/docs/_archive/03-core-features/global-search.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/03-core-features/job-tracking.md b/docs/_archive/03-core-features/job-tracking.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/04-career-workspace/career-profile.md b/docs/_archive/04-career-workspace/career-profile.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/04-career-workspace/cover-letter-builder.md b/docs/_archive/04-career-workspace/cover-letter-builder.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/04-career-workspace/cv-builder.md b/docs/_archive/04-career-workspace/cv-builder.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/04-career-workspace/cv-rendering.md b/docs/_archive/04-career-workspace/cv-rendering.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/04-career-workspace/cv-themes.md b/docs/_archive/04-career-workspace/cv-themes.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/04-career-workspace/portfolio.md b/docs/_archive/04-career-workspace/portfolio.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/05-ai/ai-architecture.md b/docs/_archive/05-ai/ai-architecture.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/05-ai/ai-features.md b/docs/_archive/05-ai/ai-features.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/05-ai/ai-providers.md b/docs/_archive/05-ai/ai-providers.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/05-ai/ai-safety.md b/docs/_archive/05-ai/ai-safety.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/06-integrations/authentication.md b/docs/_archive/06-integrations/authentication.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/06-integrations/calendar.md b/docs/_archive/06-integrations/calendar.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/06-integrations/email-providers.md b/docs/_archive/06-integrations/email-providers.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/06-integrations/job-search.md b/docs/_archive/06-integrations/job-search.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/07-security/file-security.md b/docs/_archive/07-security/file-security.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/07-security/privacy.md b/docs/_archive/07-security/privacy.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/07-security/saas-readiness.md b/docs/_archive/07-security/saas-readiness.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/07-security/security-requirements.md b/docs/_archive/07-security/security-requirements.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/08-architecture/current-architecture.md b/docs/_archive/08-architecture/current-architecture.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/08-architecture/database-model.md b/docs/_archive/08-architecture/database-model.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/08-architecture/future-architecture.md b/docs/_archive/08-architecture/future-architecture.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/08-architecture/migration-plan.md b/docs/_archive/08-architecture/migration-plan.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/10-development/coding-guidelines.md b/docs/_archive/10-development/coding-guidelines.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/10-development/deployment.md b/docs/_archive/10-development/deployment.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/_archive/10-development/testing-strategy.md b/docs/_archive/10-development/testing-strategy.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/ARCHITECTURE_EVOLUTION.md b/docs/_archive/ARCHITECTURE_EVOLUTION.md similarity index 100% rename from docs/ARCHITECTURE_EVOLUTION.md rename to docs/_archive/ARCHITECTURE_EVOLUTION.md diff --git a/docs/MERGE_REQUEST.md b/docs/_archive/MERGE_REQUEST.md similarity index 100% rename from docs/MERGE_REQUEST.md rename to docs/_archive/MERGE_REQUEST.md diff --git a/docs/PRODUCT_EVOLUTION_PLAN.md b/docs/_archive/PRODUCT_EVOLUTION_PLAN.md similarity index 100% rename from docs/PRODUCT_EVOLUTION_PLAN.md rename to docs/_archive/PRODUCT_EVOLUTION_PLAN.md diff --git a/docs/PRODUCT_RESEARCH.md b/docs/_archive/PRODUCT_RESEARCH.md similarity index 100% rename from docs/PRODUCT_RESEARCH.md rename to docs/_archive/PRODUCT_RESEARCH.md diff --git a/docs/ROADMAP.md b/docs/_archive/ROADMAP.md similarity index 100% rename from docs/ROADMAP.md rename to docs/_archive/ROADMAP.md diff --git a/docs/SECURITY_REPORT.md b/docs/_archive/SECURITY_REPORT.md similarity index 100% rename from docs/SECURITY_REPORT.md rename to docs/_archive/SECURITY_REPORT.md diff --git a/docs/SYSTEM_OVERVIEW.md b/docs/_archive/SYSTEM_OVERVIEW.md similarity index 100% rename from docs/SYSTEM_OVERVIEW.md rename to docs/_archive/SYSTEM_OVERVIEW.md diff --git a/docs/artifacts/s06-acceptance/.dev-auth-token.txt b/docs/_archive/artifacts/s06-acceptance/.dev-auth-token.txt similarity index 100% rename from docs/artifacts/s06-acceptance/.dev-auth-token.txt rename to docs/_archive/artifacts/s06-acceptance/.dev-auth-token.txt diff --git a/docs/_archive/claude.md b/docs/_archive/claude.md new file mode 100644 index 0000000..b4065c8 --- /dev/null +++ b/docs/_archive/claude.md @@ -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. \ No newline at end of file diff --git a/docs/cv-builder-parser-benchmark.md b/docs/_archive/cv-builder-parser-benchmark.md similarity index 100% rename from docs/cv-builder-parser-benchmark.md rename to docs/_archive/cv-builder-parser-benchmark.md diff --git a/docs/_archive/docs/00-ai-context.md b/docs/_archive/docs/00-ai-context.md new file mode 100644 index 0000000..3772a94 --- /dev/null +++ b/docs/_archive/docs/00-ai-context.md @@ -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. \ No newline at end of file diff --git a/docs/_archive/docs/01-glossary.md b/docs/_archive/docs/01-glossary.md new file mode 100644 index 0000000..d571841 --- /dev/null +++ b/docs/_archive/docs/01-glossary.md @@ -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. \ No newline at end of file diff --git a/docs/_archive/docs/02-product-principles.md b/docs/_archive/docs/02-product-principles.md new file mode 100644 index 0000000..b1cb49f --- /dev/null +++ b/docs/_archive/docs/02-product-principles.md @@ -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. diff --git a/docs/_archive/docs/03-design-principles.md b/docs/_archive/docs/03-design-principles.md new file mode 100644 index 0000000..93c8bca --- /dev/null +++ b/docs/_archive/docs/03-design-principles.md @@ -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. diff --git a/docs/_archive/docs/ai/overview.md b/docs/_archive/docs/ai/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/_archive/docs/ai/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ai/prompts.md b/docs/_archive/docs/ai/prompts.md new file mode 100644 index 0000000..83cfaff --- /dev/null +++ b/docs/_archive/docs/ai/prompts.md @@ -0,0 +1,3 @@ +# prompts + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ai/providers.md b/docs/_archive/docs/ai/providers.md new file mode 100644 index 0000000..23d45a1 --- /dev/null +++ b/docs/_archive/docs/ai/providers.md @@ -0,0 +1,3 @@ +# providers + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ai/safety.md b/docs/_archive/docs/ai/safety.md new file mode 100644 index 0000000..eb6fb12 --- /dev/null +++ b/docs/_archive/docs/ai/safety.md @@ -0,0 +1,3 @@ +# safety + +TODO: Complete documentation. diff --git a/docs/_archive/docs/architecture/backend.md b/docs/_archive/docs/architecture/backend.md new file mode 100644 index 0000000..87e1892 --- /dev/null +++ b/docs/_archive/docs/architecture/backend.md @@ -0,0 +1,3 @@ +# backend + +TODO: Complete documentation. diff --git a/docs/_archive/docs/architecture/current.md b/docs/_archive/docs/architecture/current.md new file mode 100644 index 0000000..28d9358 --- /dev/null +++ b/docs/_archive/docs/architecture/current.md @@ -0,0 +1,3 @@ +# current + +TODO: Complete documentation. diff --git a/docs/_archive/docs/architecture/database.md b/docs/_archive/docs/architecture/database.md new file mode 100644 index 0000000..68a2dda --- /dev/null +++ b/docs/_archive/docs/architecture/database.md @@ -0,0 +1,3 @@ +# database + +TODO: Complete documentation. diff --git a/docs/_archive/docs/architecture/deployment.md b/docs/_archive/docs/architecture/deployment.md new file mode 100644 index 0000000..ddac380 --- /dev/null +++ b/docs/_archive/docs/architecture/deployment.md @@ -0,0 +1,3 @@ +# deployment + +TODO: Complete documentation. diff --git a/docs/_archive/docs/architecture/frontend.md b/docs/_archive/docs/architecture/frontend.md new file mode 100644 index 0000000..5e7800c --- /dev/null +++ b/docs/_archive/docs/architecture/frontend.md @@ -0,0 +1,3 @@ +# frontend + +TODO: Complete documentation. diff --git a/docs/_archive/docs/architecture/future.md b/docs/_archive/docs/architecture/future.md new file mode 100644 index 0000000..4ca695e --- /dev/null +++ b/docs/_archive/docs/architecture/future.md @@ -0,0 +1,3 @@ +# future + +TODO: Complete documentation. diff --git a/docs/_archive/docs/auth/2fa.md b/docs/_archive/docs/auth/2fa.md new file mode 100644 index 0000000..48e1d76 --- /dev/null +++ b/docs/_archive/docs/auth/2fa.md @@ -0,0 +1,3 @@ +# 2fa + +TODO: Complete documentation. diff --git a/docs/_archive/docs/auth/login.md b/docs/_archive/docs/auth/login.md new file mode 100644 index 0000000..326fcef --- /dev/null +++ b/docs/_archive/docs/auth/login.md @@ -0,0 +1,3 @@ +# login + +TODO: Complete documentation. diff --git a/docs/_archive/docs/auth/oauth.md b/docs/_archive/docs/auth/oauth.md new file mode 100644 index 0000000..5831135 --- /dev/null +++ b/docs/_archive/docs/auth/oauth.md @@ -0,0 +1,3 @@ +# oauth + +TODO: Complete documentation. diff --git a/docs/_archive/docs/auth/overview.md b/docs/_archive/docs/auth/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/_archive/docs/auth/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/_archive/docs/auth/signup.md b/docs/_archive/docs/auth/signup.md new file mode 100644 index 0000000..64148d3 --- /dev/null +++ b/docs/_archive/docs/auth/signup.md @@ -0,0 +1,3 @@ +# signup + +TODO: Complete documentation. diff --git a/docs/_archive/docs/career/education.md b/docs/_archive/docs/career/education.md new file mode 100644 index 0000000..5756ec8 --- /dev/null +++ b/docs/_archive/docs/career/education.md @@ -0,0 +1,3 @@ +# education + +TODO: Complete documentation. diff --git a/docs/_archive/docs/career/experience.md b/docs/_archive/docs/career/experience.md new file mode 100644 index 0000000..a348cc0 --- /dev/null +++ b/docs/_archive/docs/career/experience.md @@ -0,0 +1,3 @@ +# experience + +TODO: Complete documentation. diff --git a/docs/_archive/docs/career/master-profile.md b/docs/_archive/docs/career/master-profile.md new file mode 100644 index 0000000..9ccff6f --- /dev/null +++ b/docs/_archive/docs/career/master-profile.md @@ -0,0 +1,3 @@ +# master-profile + +TODO: Complete documentation. diff --git a/docs/_archive/docs/career/overview.md b/docs/_archive/docs/career/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/_archive/docs/career/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/_archive/docs/career/projects.md b/docs/_archive/docs/career/projects.md new file mode 100644 index 0000000..da3d08a --- /dev/null +++ b/docs/_archive/docs/career/projects.md @@ -0,0 +1,3 @@ +# projects + +TODO: Complete documentation. diff --git a/docs/_archive/docs/career/skills.md b/docs/_archive/docs/career/skills.md new file mode 100644 index 0000000..83b2242 --- /dev/null +++ b/docs/_archive/docs/career/skills.md @@ -0,0 +1,3 @@ +# skills + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cover-letters/ai.md b/docs/_archive/docs/cover-letters/ai.md new file mode 100644 index 0000000..0c0a278 --- /dev/null +++ b/docs/_archive/docs/cover-letters/ai.md @@ -0,0 +1,3 @@ +# ai + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cover-letters/builder.md b/docs/_archive/docs/cover-letters/builder.md new file mode 100644 index 0000000..bd7421a --- /dev/null +++ b/docs/_archive/docs/cover-letters/builder.md @@ -0,0 +1,3 @@ +# builder + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cover-letters/overview.md b/docs/_archive/docs/cover-letters/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/_archive/docs/cover-letters/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/ai.md b/docs/_archive/docs/cv-builder/ai.md new file mode 100644 index 0000000..0c0a278 --- /dev/null +++ b/docs/_archive/docs/cv-builder/ai.md @@ -0,0 +1,3 @@ +# ai + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/content.md b/docs/_archive/docs/cv-builder/content.md new file mode 100644 index 0000000..533130a --- /dev/null +++ b/docs/_archive/docs/cv-builder/content.md @@ -0,0 +1,3 @@ +# content + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/customisation.md b/docs/_archive/docs/cv-builder/customisation.md new file mode 100644 index 0000000..73b24e8 --- /dev/null +++ b/docs/_archive/docs/cv-builder/customisation.md @@ -0,0 +1,3 @@ +# customisation + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/exports.md b/docs/_archive/docs/cv-builder/exports.md new file mode 100644 index 0000000..247610d --- /dev/null +++ b/docs/_archive/docs/cv-builder/exports.md @@ -0,0 +1,3 @@ +# exports + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/overview.md b/docs/_archive/docs/cv-builder/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/_archive/docs/cv-builder/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/rendering.md b/docs/_archive/docs/cv-builder/rendering.md new file mode 100644 index 0000000..007182c --- /dev/null +++ b/docs/_archive/docs/cv-builder/rendering.md @@ -0,0 +1,3 @@ +# rendering + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/themes.md b/docs/_archive/docs/cv-builder/themes.md new file mode 100644 index 0000000..6c609bb --- /dev/null +++ b/docs/_archive/docs/cv-builder/themes.md @@ -0,0 +1,3 @@ +# themes + +TODO: Complete documentation. diff --git a/docs/_archive/docs/cv-builder/workflow.md b/docs/_archive/docs/cv-builder/workflow.md new file mode 100644 index 0000000..8c16632 --- /dev/null +++ b/docs/_archive/docs/cv-builder/workflow.md @@ -0,0 +1,3 @@ +# workflow + +TODO: Complete documentation. diff --git a/docs/_archive/docs/documentation-rules.md b/docs/_archive/docs/documentation-rules.md new file mode 100644 index 0000000..4ef5373 --- /dev/null +++ b/docs/_archive/docs/documentation-rules.md @@ -0,0 +1,3 @@ +# documentation-rules + +TODO: Complete documentation. diff --git a/docs/_archive/docs/jobs/applications.md b/docs/_archive/docs/jobs/applications.md new file mode 100644 index 0000000..d14164a --- /dev/null +++ b/docs/_archive/docs/jobs/applications.md @@ -0,0 +1,3 @@ +# applications + +TODO: Complete documentation. diff --git a/docs/_archive/docs/jobs/companies.md b/docs/_archive/docs/jobs/companies.md new file mode 100644 index 0000000..7aaac6e --- /dev/null +++ b/docs/_archive/docs/jobs/companies.md @@ -0,0 +1,3 @@ +# companies + +TODO: Complete documentation. diff --git a/docs/_archive/docs/jobs/followups.md b/docs/_archive/docs/jobs/followups.md new file mode 100644 index 0000000..32fb118 --- /dev/null +++ b/docs/_archive/docs/jobs/followups.md @@ -0,0 +1,3 @@ +# followups + +TODO: Complete documentation. diff --git a/docs/_archive/docs/jobs/job-import.md b/docs/_archive/docs/jobs/job-import.md new file mode 100644 index 0000000..0dc11f4 --- /dev/null +++ b/docs/_archive/docs/jobs/job-import.md @@ -0,0 +1,3 @@ +# job-import + +TODO: Complete documentation. diff --git a/docs/_archive/docs/jobs/job-search.md b/docs/_archive/docs/jobs/job-search.md new file mode 100644 index 0000000..d759b27 --- /dev/null +++ b/docs/_archive/docs/jobs/job-search.md @@ -0,0 +1,3 @@ +# job-search + +TODO: Complete documentation. diff --git a/docs/_archive/docs/jobs/overview.md b/docs/_archive/docs/jobs/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/_archive/docs/jobs/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/_archive/docs/product/business-model.md b/docs/_archive/docs/product/business-model.md new file mode 100644 index 0000000..fd8932f --- /dev/null +++ b/docs/_archive/docs/product/business-model.md @@ -0,0 +1,3 @@ +# business-model + +TODO: Complete documentation. diff --git a/docs/_archive/docs/product/goals.md b/docs/_archive/docs/product/goals.md new file mode 100644 index 0000000..b5ee870 --- /dev/null +++ b/docs/_archive/docs/product/goals.md @@ -0,0 +1,3 @@ +# goals + +TODO: Complete documentation. diff --git a/docs/_archive/docs/product/mission.md b/docs/_archive/docs/product/mission.md new file mode 100644 index 0000000..a8f6766 --- /dev/null +++ b/docs/_archive/docs/product/mission.md @@ -0,0 +1,3 @@ +# mission + +TODO: Complete documentation. diff --git a/docs/_archive/docs/product/non-goals.md b/docs/_archive/docs/product/non-goals.md new file mode 100644 index 0000000..b62265c --- /dev/null +++ b/docs/_archive/docs/product/non-goals.md @@ -0,0 +1,3 @@ +# non-goals + +TODO: Complete documentation. diff --git a/docs/_archive/docs/product/personas.md b/docs/_archive/docs/product/personas.md new file mode 100644 index 0000000..96f2c8d --- /dev/null +++ b/docs/_archive/docs/product/personas.md @@ -0,0 +1,3 @@ +# personas + +TODO: Complete documentation. diff --git a/docs/_archive/docs/product/roadmap.md b/docs/_archive/docs/product/roadmap.md new file mode 100644 index 0000000..6e607ce --- /dev/null +++ b/docs/_archive/docs/product/roadmap.md @@ -0,0 +1,3 @@ +# roadmap + +TODO: Complete documentation. diff --git a/docs/_archive/docs/product/vision.md b/docs/_archive/docs/product/vision.md new file mode 100644 index 0000000..8496fc8 --- /dev/null +++ b/docs/_archive/docs/product/vision.md @@ -0,0 +1,3 @@ +# vision + +TODO: Complete documentation. diff --git a/docs/_archive/docs/research/flowcv.md b/docs/_archive/docs/research/flowcv.md new file mode 100644 index 0000000..7a8d94b --- /dev/null +++ b/docs/_archive/docs/research/flowcv.md @@ -0,0 +1,3 @@ +# flowcv + +TODO: Complete documentation. diff --git a/docs/_archive/docs/research/jobscan.md b/docs/_archive/docs/research/jobscan.md new file mode 100644 index 0000000..d7503c1 --- /dev/null +++ b/docs/_archive/docs/research/jobscan.md @@ -0,0 +1,3 @@ +# jobscan + +TODO: Complete documentation. diff --git a/docs/_archive/docs/research/novoresume.md b/docs/_archive/docs/research/novoresume.md new file mode 100644 index 0000000..fa701c3 --- /dev/null +++ b/docs/_archive/docs/research/novoresume.md @@ -0,0 +1,3 @@ +# novoresume + +TODO: Complete documentation. diff --git a/docs/_archive/docs/research/reactive-resume.md b/docs/_archive/docs/research/reactive-resume.md new file mode 100644 index 0000000..1861b90 --- /dev/null +++ b/docs/_archive/docs/research/reactive-resume.md @@ -0,0 +1,3 @@ +# reactive-resume + +TODO: Complete documentation. diff --git a/docs/_archive/docs/security/overview.md b/docs/_archive/docs/security/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/_archive/docs/security/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/_archive/docs/security/privacy.md b/docs/_archive/docs/security/privacy.md new file mode 100644 index 0000000..d543b7e --- /dev/null +++ b/docs/_archive/docs/security/privacy.md @@ -0,0 +1,3 @@ +# privacy + +TODO: Complete documentation. diff --git a/docs/_archive/docs/security/uploads.md b/docs/_archive/docs/security/uploads.md new file mode 100644 index 0000000..e3bbbc4 --- /dev/null +++ b/docs/_archive/docs/security/uploads.md @@ -0,0 +1,3 @@ +# uploads + +TODO: Complete documentation. diff --git a/docs/_archive/docs/security/validation.md b/docs/_archive/docs/security/validation.md new file mode 100644 index 0000000..0a15e2b --- /dev/null +++ b/docs/_archive/docs/security/validation.md @@ -0,0 +1,3 @@ +# validation + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ux/accessibility.md b/docs/_archive/docs/ux/accessibility.md new file mode 100644 index 0000000..197913e --- /dev/null +++ b/docs/_archive/docs/ux/accessibility.md @@ -0,0 +1,3 @@ +# accessibility + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ux/dashboard.md b/docs/_archive/docs/ux/dashboard.md new file mode 100644 index 0000000..9fb5df7 --- /dev/null +++ b/docs/_archive/docs/ux/dashboard.md @@ -0,0 +1,3 @@ +# dashboard + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ux/design-system.md b/docs/_archive/docs/ux/design-system.md new file mode 100644 index 0000000..eabd28c --- /dev/null +++ b/docs/_archive/docs/ux/design-system.md @@ -0,0 +1,3 @@ +# design-system + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ux/navigation.md b/docs/_archive/docs/ux/navigation.md new file mode 100644 index 0000000..fd5f437 --- /dev/null +++ b/docs/_archive/docs/ux/navigation.md @@ -0,0 +1,3 @@ +# navigation + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ux/onboarding.md b/docs/_archive/docs/ux/onboarding.md new file mode 100644 index 0000000..1da0892 --- /dev/null +++ b/docs/_archive/docs/ux/onboarding.md @@ -0,0 +1,3 @@ +# onboarding + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ux/profile.md b/docs/_archive/docs/ux/profile.md new file mode 100644 index 0000000..cf52bcb --- /dev/null +++ b/docs/_archive/docs/ux/profile.md @@ -0,0 +1,3 @@ +# profile + +TODO: Complete documentation. diff --git a/docs/_archive/docs/ux/settings.md b/docs/_archive/docs/ux/settings.md new file mode 100644 index 0000000..57308cf --- /dev/null +++ b/docs/_archive/docs/ux/settings.md @@ -0,0 +1,3 @@ +# settings + +TODO: Complete documentation. diff --git a/docs/_archive/documentation-rules.md b/docs/_archive/documentation-rules.md new file mode 100644 index 0000000..741216b --- /dev/null +++ b/docs/_archive/documentation-rules.md @@ -0,0 +1,171 @@ +# Jobjakt Documentation Rules + +## Purpose + +This document defines how documentation in this repository should be created and maintained. + +Documentation is considered part of the product. + +--- + +# Documentation Principles + +## Source of truth + +Product decisions: + +- Product documentation +- ADRs + +Technical reality: + +- Codebase +- Database +- Configuration + +If documentation and code disagree: + +- Code represents current reality. +- Documentation represents intended direction. + +The difference must be documented. + +--- + +# Document Types + +## Product Documents + +Describe: + +- Why something exists. +- User problems. +- Expected behaviour. +- Business goals. + +Examples: + +- Product vision. +- User journeys. +- Feature requirements. + +--- + +## Technical Documents + +Describe: + +- How something works. +- Architecture. +- Implementation details. + +Examples: + +- API documentation. +- Database models. +- Services. + +--- + +## Decision Records + +Architecture Decision Records explain important choices. + +Format: + + +Context + +Decision + +Alternatives considered + +Consequences + + +--- + +# Required Document Sections + +Where relevant documents should include: + +## Purpose + +Why does this exist? + +## User Value + +What problem does this solve? + +## User Journey + +How does the user interact with it? + +## Requirements + +What must happen? + +## Non Requirements + +What should not happen? + +## Technical Notes + +Implementation considerations. + +## Future Considerations + +Possible expansion. + +## Open Questions + +Unknown decisions. + +--- + +# AI Development Rules + +AI assistants must: + +- Read relevant documentation before coding. +- Analyse existing implementation. +- Avoid unnecessary rewrites. +- Preserve existing functionality. +- Update documentation after significant changes. + +--- + +# Updating Documentation + +When implementing features: + +Update: + +- Relevant feature documentation. +- Architecture documentation. +- ADRs if decisions change. + +--- + +# Accuracy + +Never invent: + +- Existing features. +- Database fields. +- APIs. +- User behaviour. + +Separate: + +Fact: + +Something confirmed in code. + +Recommendation: + +Suggested improvement. + +Future idea: + +Possible later feature. \ No newline at end of file diff --git a/docs/gmail-correspondence-phase1.md b/docs/_archive/gmail-correspondence-phase1.md similarity index 100% rename from docs/gmail-correspondence-phase1.md rename to docs/_archive/gmail-correspondence-phase1.md diff --git a/docs/_archive/infrastructure-investigation.md b/docs/_archive/infrastructure-investigation.md new file mode 100644 index 0000000..6b52288 --- /dev/null +++ b/docs/_archive/infrastructure-investigation.md @@ -0,0 +1,581 @@ +# Infrastructure Investigation — Deployment Failure (Server Unresponsive, Containers Stopped) + +**Date:** 2026-07-13 +**Trigger:** Latest deployment failed; server became unresponsive; some Docker containers stopped unexpectedly; +pushes occasionally fail; deployment occasionally hangs. Scope later widened by the user: the same host also +runs Jellyfin/Radarr/Sonarr and other unrelated services, which show the identical symptom (containers not +coming back after reboot despite `restart: always`/`unless-stopped`) — confirming this is a **host-wide** +problem, not specific to the jobtracker app. +**Mode:** Investigation only. No changes, restarts, deletions, prunes, installs, or fixes were made. No +configuration was modified. All commands run over SSH were read-only (`journalctl`, `docker ps`/`inspect`, +`free`, `df`, `uptime`, `crontab -l`, `cat`) — nothing was written, restarted, or deleted on the server. + +--- + +## Method note — two phases of this investigation + +**Phase 1 (static, no server access):** initial pass was done entirely from the local development checkout, +without live access to the deployment host — its SSH credentials are Gitea Actions secrets not present on this +machine. That phase built a hypothesis from `docker-compose.yml`, `Dockerfile`s, `deploy/deploy.sh`, the CI +workflow, and git history alone (see "Phase 1 findings" below — still valid as contributing/secondary factors). + +**Phase 2 (live, with server access):** the user then provided direct SSH access (`pi@192.168.50.13`) and noted +the problem isn't limited to this app. This phase found the actual, currently-reproducing root cause with +timestamped live evidence (kernel/systemd journal, `docker inspect`, `docker ps -a` across every Compose stack +on the box) — see "**Primary Finding — systemd D-Bus/session-bus degradation (host-wide)**" immediately below. +This supersedes Phase 1's top-ranked hypothesis (host-level resource exhaustion): the box has 31GB RAM (23GB +free), 6 cores at ~10-25% load, and 8GB of completely unused swap — it is not resource-starved. Phase 1's +findings about the *jobtracker deploy process specifically* (no health checks, building images on the live +host, etc.) remain valid and worth fixing, but they are not the primary cause of the reported outage. + +--- + +## Primary Finding — systemd D-Bus/session-bus degradation (host-wide, confirmed live) + +**Issue:** `systemd`'s own D-Bus/session-bus subsystem (`org.freedesktop.systemd1`) is intermittently failing to +respond within its 25-second timeout, host-wide, to **every kind of request that needs it** — not just Docker. +This breaks three unrelated things by the same mechanism: + +1. **New container creation and container restarts.** Docker's `systemd` cgroup driver asks systemd (over + D-Bus) to create a transient `.scope` unit for every container start/restart. When that call times out, + the container fails to start with `OCI runtime create failed: ... unable to start unit + "docker-.scope" ... Failed to activate service 'org.freedesktop.systemd1': timed out + (service_start_timeout=25000ms)`. This is Docker's own restart-manager logging the exact same error when + trying to *restart* an existing container — i.e. `restart: always`/`unless-stopped` cannot save a container + if the D-Bus call underneath it is what's failing. +2. **New SSH sessions.** `systemd-logind` needs the same D-Bus mechanism to register a session scope for every + new login. Live log during this investigation: `sshd[...]: pam_systemd(sshd:session): Failed to create + session: Connection timed out` and `systemd-logind[1031]: Failed to start session scope session-6497.scope: + Failed to activate service 'org.freedesktop.systemd1': timed out`. If this gets worse, it can make the box + genuinely un-SSH-able — a very plausible reading of "server becomes unresponsive." +3. **systemd-journald's own internal watchdog heartbeat.** `systemd-journald[432]: Failed to send WATCHDOG=1 + notification message: Transport endpoint is not connected` — journald cannot even talk to systemd about its + own liveness. "Transport endpoint is not connected" (`ENOTCONN`) is a broken-socket error, not a busy/slow + one — consistent with systemd's D-Bus broker itself being unhealthy, not merely overloaded. + +**Evidence (live, timestamped, gathered 2026-07-13 ~15:30-15:46 local server time):** +``` +Jul 13 14:53:35 mediaserver dockerd[1326]: ... restartmanger wait error: failed to create task for container: + ... Failed to activate service 'org.freedesktop.systemd1': timed out (service_start_timeout=25000ms) +Jul 13 15:37:01 mediaserver systemd-journald[432]: Failed to send WATCHDOG=1 notification message: + Transport endpoint is not connected +Jul 13 15:38:09 mediaserver systemd-logind[1031]: Failed to start session scope session-6481.scope: + Connection timed out +Jul 13 15:38:09 mediaserver sshd[1994140]: pam_systemd(sshd:session): Failed to create session: Connection timed out +Jul 13 15:46:20 mediaserver sshd[2004289]: pam_systemd(sshd:session): Failed to create session: + Failed to activate service 'org.freedesktop.systemd1': timed out (service_start_timeout=25000ms) +Jul 13 15:46:20 mediaserver systemd-logind[1031]: Failed to start session scope session-6497.scope: + Failed to activate service 'org.freedesktop.systemd1': timed out (service_start_timeout=25000ms) +``` +The `systemd-journald` watchdog failure recurred roughly every 90 seconds to 2 minutes throughout the ~10-minute +observation window — i.e. this was actively reproducing *while the investigation was running*, not a rare blip. + +`docker inspect deunhealth` showed `RestartCount=389`, `StartedAt=2026-07-13T12:40:14Z` — a container that has +been crash-restart-looping 389 times (and counting). One of its restart attempts is directly logged as failing +for the *same* D-Bus-timeout reason (`restartmanger wait error ... Failed to activate service +'org.freedesktop.systemd1'`). Ironically, `deunhealth` (`qmcgaw/deunhealth`) is itself a watchdog tool whose job +is auto-restarting other unhealthy containers — so the host's auto-healing mechanism is caught in the same +degraded-D-Bus bottleneck it exists to work around. + +The very first occurrence of this exact Docker/systemd1 timeout in the journal dates back to **`Apr 29 23:25:52`** +— **this has been an intermittent problem for roughly 2.5 months**, not something new introduced by the latest +deployment. Only 7 occurrences matched in Docker's own log across the last 48 hours specifically, but the +broader journald-watchdog/logind symptom was firing every ~90 seconds during the live observation window, +suggesting either a currently-active flare-up or a much higher background rate than Docker's log alone shows +(Docker only logs the timeout when *it itself* happens to need D-Bus at that exact moment; logind/journald hit +it far more often simply because they touch D-Bus more frequently). + +**Contributing factor (not necessarily the root cause, but plausible amplifier):** this host runs **37 containers +across 7 separate Docker Compose projects** simultaneously — `app` (jobtracker prod, confirmed to be +`/opt/job-tracker/app`, matching this repo's deploy target exactly), `deploy` (a "resumesite" project), `docker` +(the main Jellyfin/Radarr/Sonarr media stack, 22 containers), `inboxintel`, `mealie`, `mediaserver` (a *second*, +apparently overlapping media-stack definition), and a dead leftover `jobtracker` project at a different, +stale path (`/home/pi/jobtracker/JobTracker/docker-compose.yml`) — clearly an old pre-migration deployment +location nobody cleaned up. On top of that, two cron jobs auto-deploy other projects **every 1-2 minutes**: +``` +*/2 * * * * /opt/inboxintel/deploy/auto-deploy.sh +* * * * * /opt/resumesite/deploy/autodeploy.sh +``` +Gitea Actions CI job containers were also observed live in the Docker log (`GITEA-ACTIONS-TASK-1083...JOB-deploy`, +`GITEA-ACTIONS-TASK-1084...JOB-test`) — confirming the self-hosted CI runner from Phase 1's investigation +**is this same box**. Combined with 389+ restart attempts from one crash-looping container alone, this is a lot +of transient-unit churn (container creates/destroys, session scopes, per-minute cron-triggered rebuilds) hitting +systemd's D-Bus continuously. High churn is a well-known trigger for exactly this class of systemd bug on some +Ubuntu/systemd version combinations (the box is on kernel `6.8.0-110-generic`) — a D-Bus broker or transient-unit +bookkeeping resource (e.g. an internal queue, a leaked file descriptor, an fd/inotify limit) getting exhausted +under sustained high creation/deletion rate. + +**Impact:** This single mechanism plausibly explains **all four** originally-reported symptoms at once, across +**every app on the host**, not just jobtracker: +- "Some Docker containers stop unexpectedly" — a restart attempt loses the D-Bus race and the container simply + stays down, non-deterministically (whichever container's restart happens to line up with a bad D-Bus moment). +- "Deployment occasionally hangs" — `docker compose up --force-recreate` creates new containers, each requiring + the same D-Bus call; a deploy can stall or fail here exactly like any other container start. +- "The server becomes unresponsive" — new SSH sessions and journald's own health-check both depend on the same + broken mechanism; in a bad enough episode this could make the box very difficult to even log into. +- "Pushes occasionally fail" — consistent with a CI job container (visible running on this same host) hitting + the same timeout during its own container lifecycle. + +**Confidence:** **High** — this is not inferred from static config or git history, it is directly observed, +timestamped, live log evidence, reproducing during the investigation itself, across three independent subsystems +(Docker, sshd/logind, journald) that all point to the same underlying D-Bus failure. + +**What would raise this from High to Confirmed:** root-level `journalctl` access (this investigation ran as the +unprivileged `pi` user — `sudo` requires a password not available non-interactively over this SSH session, so +kernel-level (`dmesg`) OOM/hardware logs and the D-Bus broker's *own* service logs (`systemd-logind.service`, +`dbus.service`/`dbus-broker.service`) were not reachable). See "What I could not verify" for the exact commands +to run with root access. + +--- + +## Phase 1 findings (static analysis, secondary/contributing — still valid) + +These were the findings from before server access was available. They remain accurate descriptions of the +jobtracker deploy process's own gaps, and are worth fixing regardless of the primary systemd finding above, but +they are not the main explanation for the reported outage. + +**Overall health assessment (Phase 1):** The deployment pipeline and container topology are functionally +reasonable (sensible service split, `restart: unless-stopped`, a health check on the AI service, retry-once +logic bolted onto several known-flaky build steps) but have **no resource governance** anywhere — no +per-container CPU/memory limits, no health check on the two services that actually serve user traffic +(`backend`, `frontend`), and a deploy process that **builds new container images on the same host that is +simultaneously serving production traffic**, with zero headroom reserved for that build. Git history +independently documents at least five separate incidents over the last ~8 days (NuGet package-integrity +corruption, npm `SIGSEGV`, .NET SDK tool-cache corruption, and a webpack/Terser build that "died silently... +OOM/SIGSEGV signature", per the workflow's own comment) on this same runner/host class, each patched with a +"retry once" band-aid rather than a resource-level fix. **Note:** live data shows the host is not actually +memory-constrained (31GB RAM, 23GB free) — these build-tool crashes are more likely a *symptom* of the same +D-Bus/cgroup-scope-creation failures documented above (a build step that needs to spawn a container/process via +a cgroup scope hits the same timeout) than of genuine memory exhaustion. + +**Most likely root cause:** Host-level memory/CPU exhaustion during the build phase of deployment, on hardware +that has already independently shown itself to be resource-constrained, with no per-container limits to contain +a spike and no health checks to catch a hung-but-technically-running container. This is consistent with all +four reported symptoms (containers stopping unexpectedly, deployment hanging, server unresponsiveness, and +occasional push/CI failures) being **the same underlying resource-starvation problem manifesting at different +pipeline stages**, not four unrelated problems. + +**Confidence:** High for "the host is resource-constrained and deploys build under live load with no limits." +Medium for "this is specifically what caused the *latest* incident" (plausible and consistent with the evidence +trail, but not confirmed against that incident's actual logs, which I could not access). + +--- + +## Findings + +### Finding 1 — No CPU/memory limits on any container + +**Issue:** `docker-compose.yml` defines four services (`backend`, `frontend`, `ai-service`, `ollama`) and none +of them set `mem_limit`, `cpus`, or a `deploy.resources.limits` block. Any single container is free to consume +all available host memory. + +**Evidence:** +```yaml +# docker-compose.yml — repeated across every service, no limits anywhere +backend: + build: ... + restart: unless-stopped + # no mem_limit / cpus / deploy.resources +``` +Confirmed by reading the full file (`docker-compose.yml`, all four service blocks + the opt-in `ollama` +profile) — no resource stanza appears once in the file. + +**Impact:** On a memory-constrained host, one container growing unbounded (or several building/starting +concurrently) triggers the Linux OOM killer. The OOM killer scores *all* processes on the host, not just the +one causing pressure — it can and does kill processes that had nothing to do with the spike. This directly +explains "**some** containers stop unexpectedly" (non-deterministic, not all containers, not the one under +load) rather than a clean, predictable failure of the actual offending service. + +**Confidence:** High (structural fact, verified by reading the file in full). + +--- + +### Finding 2 — No health check on `backend` or `frontend`; only `ai-service` and `ollama` have one + +**Issue:** `ai-service` has a proper `healthcheck` (HTTP GET `/health`, 30s interval). `ollama` has one too +(`ollama list`, 20s interval). `backend` and `frontend` — the two services that actually serve the product and +its API — have **none**. + +**Evidence:** Full `docker-compose.yml` read; `healthcheck:` appears exactly twice, on `ai-service` and +`ollama`. `deploy/deploy.sh`'s own post-deploy check confirms the gap — it only asks Docker for the container +*state*, not health: +```bash +backend_status="$(compose ps backend --format '{{.State}}' 2>/dev/null | head -n 1 | tr '[:upper:]' '[:lower:]')" +if [ "$backend_status" != "running" ]; then + echo "Backend service is not healthy after deploy (state: ${backend_status:-unknown})." +``` +`State` reports "running" the instant the process starts — it says nothing about whether the ASP.NET Core app +inside has actually finished startup, bound its port, or is responding. A backend that starts, then hangs +during EF Core migration or hits a startup exception loop *inside* the process without exiting, would report +`running` and pass this check while being completely unresponsive to real traffic. + +**Impact:** Directly explains "**deployment occasionally hangs**" and false-positive successful deploys — the +gate that's supposed to catch a broken backend cannot detect the specific failure mode of "process is alive but +not serving," which is exactly what host resource starvation during startup would produce (slow/stuck EF +migrations, thread-pool starvation under memory pressure, etc.). + +**Confidence:** High (structural fact; the reasoning about *why* it matters is inference, not confirmed against +the actual incident logs). + +--- + +### Finding 3 — Documented history of resource-starvation symptoms on this same host class + +**Issue:** Git history shows a cluster of CI/deploy resilience patches, each independently describing a +resource-exhaustion signature, spanning 2026-07-05 through 2026-07-12: + +| Date | Commit | Symptom described in the commit/comment | +|---|---|---| +| 07-05 | `63e0300` / `e90835b` | NuGet package integrity check failures (`NU3008`) during `dotnet publish` on the build host — "transient download corruption on the build host" | +| 07-06 | `4b38f7c` | "`npm ci` occasionally segfaults on the runner (SIGSEGV/139, **a memory/native flake**)" | +| 07-11 | `fc35601` / `35eaef9` | `actions/setup-dotnet` "intermittently leaves a partial extraction in the shared tool-cache (`tar: Cannot open: File exists`) or corrupts the SDK download" | +| 07-11 | `4f98195` | CRA/webpack build "has repeatedly died silently on this runner with no error output (**OOM/SIGSEGV signature**...)" | +| 07-12 | `86cdafb` | A different class of bug (missing `.npmrc` in Docker build context) — not resource-related, included for completeness | + +Every one of these except the last was patched with **retry-once logic**, not a resource-level fix. `deploy.sh` +itself carries the same pattern for the *production* build step, with an explicit comment on both retry paths: + +```bash +echo "docker compose build for core services failed. Attempting one cleanup + retry +because layer extraction can fail on constrained hosts." +``` +("constrained hosts" — the deploy script's own author already believed this.) + +**Evidence:** `git log --oneline --grep=... --since="30 days ago"` (see command output used in this +investigation); direct commit message and inline-comment quotes above. + +**Impact:** This is the strongest available evidence that the underlying host (CI runner and/or production +deploy target — the two may be the same self-hosted machine, common in small-scale setups; I could not confirm +either way without server access) has **insufficient memory headroom for its build workload**, as a standing, +recurring condition rather than a one-off. + +**Confidence:** High that the pattern exists and points to memory pressure. Medium on "this is the same host as +the one that just failed" — plausible, not confirmed. + +--- + +### Finding 4 — Deploy builds new images on the same host serving live traffic, with no isolation + +**Issue:** `deploy/deploy.sh` runs `docker compose build backend frontend` (a full `dotnet publish` inside the +`mcr.microsoft.com/dotnet/sdk:9.0` image, plus a full `npm run build` inside `node:20-alpine`) directly on the +deploy target, **while the currently-running containers from the previous deploy are still serving production +traffic** (they aren't stopped until `compose up -d --force-recreate` afterward). Both build steps are +independently already known to be memory-hungry enough to need special handling: + +```dockerfile +# job-tracker-ui/Dockerfile +shm_size: '1gb' +# Next's build type-checker needs more than Docker's default 64MB /dev/shm; too little +# causes a SIGSEGV during `npm run build`. +``` + +**Evidence:** `deploy/deploy.sh` full read (`build_core_with_recovery`, `compose build backend frontend` before +any container is stopped); `job-tracker-ui/Dockerfile` and its `docker-compose.yml` `shm_size` override, with +the SIGSEGV comment. + +**Impact:** This is a **concurrent resource spike**: two separate heavy compiler toolchains (the .NET SDK +compiler/publish pipeline and webpack+Terser minification) running back-to-back or overlapping, on a host with +no memory limits configured (Finding 1), while that same host is also running the live app for existing users. +On already-constrained hardware (Finding 3), this is a very plausible trigger for the host itself becoming +unresponsive during a deploy, not just one container failing. + +**Confidence:** High that this is how the deploy works (verified by reading the script). Medium-High that it is +a primary contributor to the reported "server becomes unresponsive" symptom specifically during deploys. + +--- + +### Finding 5 — AI service memory footprint is opaque and possibly significant + +**Issue:** `ai-service` loads a `distilbart-cnn-12-6` summarization model into RAM via CPU-only PyTorch +(`--extra-index-url https://download.pytorch.org/whl/cpu` in `tools/summarizer/Dockerfile`), and can optionally +also run a bundled `ollama` service with a `qwen2.5:7b` model (a 7B-parameter LLM, several GB of RAM/VRAM even +quantized) if the `bundled-ollama` Compose profile is active in production. Neither has a memory limit (Finding +1). Whether the bundled-Ollama profile or an external/cloud AI provider (`AI_PROVIDER=gemini`/`groq`, added +earlier this session) is actually active in the live `.env` could not be determined from this repo — that file +lives only on the server at `/opt/job-tracker/shared/.env`, outside version control. + +**Evidence:** `tools/summarizer/Dockerfile`, `tools/summarizer/app.py` (`SKIP_MODEL_LOAD` / `EAGER_MODEL_LOAD` +env vars, `torch.device`), `docker-compose.yml` (`ollama` service, `profiles: ["bundled-ollama"]`, +`gpus: all`), `deploy/README.md` example `.env` (shows `OLLAMA_MODEL=qwen2.5:7b` as the documented default). + +**Impact:** If the local-Ollama path is active rather than a cloud provider, this is a large, constant RAM +baseline on top of everything else, on a host already shown to be tight on memory. Cannot be confirmed without +reading the live `.env` and `docker stats` output on the server. + +**Confidence:** Medium — plausible contributor, not confirmed as active in the current production configuration. + +--- + +### Finding 6 — Database and reverse proxy are entirely outside this repository's visibility + +**Issue:** Per `deploy/README.md`, MariaDB and the reverse proxy (Nginx/Caddy/Traefik) are **required** but run +as separate, external infrastructure connected only via the `jobtracker_shared` external Docker network — their +compose files, resource limits, health checks, and logs are not part of this repository and were not +accessible during this investigation. + +**Evidence:** +```yaml +# docker-compose.yml +networks: + shared_services: + external: true + name: jobtracker_shared +``` +```markdown +# deploy/README.md +Requirements: +- reverse proxy in front (Nginx, Caddy, or Traefik) +- network connectivity from the backend container to your `mariadb` container/service +``` + +**Impact:** A real blind spot. If the database or reverse proxy container itself is what's crashing (rather +than `backend`/`frontend`/`ai-service`), nothing in this repo would show it, and the symptoms reported (server +unresponsive, containers stopping) are equally consistent with a database under memory/connection pressure as +with the app containers themselves. + +**Confidence:** N/A (this is a coverage gap, not a finding about behavior) — flagged as high-priority to close +in the remediation plan. + +--- + +### Finding 7 — Failed-build cleanup is reactive only, not proactive + +**Issue:** `docker builder prune -af` and `docker system prune -f` only run **after a build has already +failed**, as part of the one-shot retry path in `deploy/deploy.sh`. There is no proactive/scheduled cleanup of +dangling images, stopped containers, or build cache between deploys. + +**Evidence:** `deploy/deploy.sh`, `build_core_with_recovery()` / `build_ai_with_recovery()` — prune calls exist +only inside the `if compose build ...; then return 0; fi` failure branch. + +**Impact:** Each build (successful or not) leaves the previous image's now-dangling layers on disk. Combined +with the documented history of build failures (Finding 3), disk usage from accumulated dangling +images/layers/cache could grow over weeks. A full disk independently causes container crashes and can make a +host appear "unresponsive" (Docker daemon operations stall when the disk backing `/var/lib/docker` is full). + +**Confidence:** Medium — real risk pattern, but disk usage itself was not observable from here (would need +`df -h` and `docker system df` on the server). + +--- + +### Finding 8 — `git reset --hard` + `git clean -fd` on every deploy + +**Issue:** The deploy step runs `git reset --hard ${{ github.sha }}` followed by `git clean -fd` against +`/opt/job-tracker/app` on every single deploy to `main`. + +**Evidence:** `.gitea/workflows/ci-deploy.yml`, `deploy` job, `script:` block. + +**Impact:** Not a likely cause of *this* incident, but a standing operational risk: any file manually placed in +that checkout for debugging (a log dump, a temporary config override, a core dump for post-mortem analysis) +that isn't committed is **silently and permanently deleted** on the next push to `main`. If anyone tried to +capture server-side evidence for a previous incident this way, it would not have survived to be reviewed later. +Flagged here because it's directly relevant to *investigability* going forward, even though it's not a root +cause of the crash itself. + +**Confidence:** High (structural fact). Not applicable as a root cause of the outage. + +--- + +### Finding 9 — nginx `/api/` proxy has no explicit timeout tuning + +**Issue:** `job-tracker-ui/nginx.conf`'s `/api/` `proxy_pass` block sets no `proxy_read_timeout`, +`proxy_connect_timeout`, or `proxy_send_timeout` — nginx defaults (60s) apply. + +**Evidence:** Full `nginx.conf` read (10 lines, reproduced above in the investigation transcript). + +**Impact:** Low likelihood of being a root cause on its own, but under host memory pressure (Findings 1–4), a +backend under memory/swap pressure could respond slowly enough to hit this default timeout, surfacing as +intermittent 504s to users — a plausible explanation for "pushes occasionally fail" if that phrase also +describes user-facing request failures around deploy time, not just `git push`/CI failures. Worth tuning +regardless, low effort. + +**Confidence:** Low as a root cause; Medium as a contributing/compounding factor. + +--- + +## Root Cause Analysis (ranked) + +### 1. Host-wide systemd D-Bus/session-bus degradation — **HIGH confidence, directly observed live** +**Why it's likely:** Not inferred — directly observed in the live journal during this investigation, recurring +every ~90 seconds to 2 minutes across three independent subsystems (Docker's cgroup-scope creation, sshd/logind +session-scope creation, journald's own watchdog heartbeat) that all fail with the same underlying D-Bus timeout +or a broken-socket (`ENOTCONN`) error. First occurrence in the retained journal dates back to `Apr 29 23:25:52` +— an intermittent, ~2.5-month-old condition, not something introduced by the latest deploy. +**Supporting evidence:** See "Primary Finding" above in full — direct log quotes, `docker inspect` output +(`deunhealth` at 389 restarts), `docker compose ls -a` showing 37 containers across 7 Compose projects on one +host, and confirmation that the Gitea Actions CI runner from Phase 1 is this same box. +**Alternative explanations:** Genuine host memory/CPU exhaustion (Phase 1's original top hypothesis) is +directly contradicted by live data — 31GB RAM with 23GB free, load average 0.09-0.27, 8GB of completely unused +swap. A kernel/hardware fault is possible but unconfirmed (would need `dmesg`/root `journalctl -k`, not +reachable non-interactively as the unprivileged `pi` user in this session). The high container/cron/CI churn on +this box (Finding 3 below, restated) is the most likely *amplifier*, not necessarily the sole trigger — a +D-Bus/dbus-broker bug triggered or worsened by sustained high transient-unit creation/deletion rate is a +well-known class of systemd issue on some version combinations. + +### 2. High container and deploy churn amplifying #1 — **MEDIUM-HIGH confidence** +**Why it's likely:** 37 containers across 7 Compose projects (including a dead, stale, seemingly-forgotten +second `jobtracker` project at `/home/pi/jobtracker/JobTracker/`, and what look like two overlapping media-stack +definitions — `docker` and `mediaserver`), two other projects' cron jobs auto-deploying **every 1-2 minutes**, +Gitea Actions CI containers spinning up/down on the same host, and at least one container crash-looping 389+ +times — all repeatedly hitting the same D-Bus-mediated cgroup-scope machinery Finding 1 shows is unhealthy. +**Supporting evidence:** `docker compose ls -a` output, crontab entries, `deunhealth` restart count — see +Primary Finding above. +**Alternative explanations:** Could be a red herring if the D-Bus issue turns out to be purely a systemd/kernel +bug unrelated to load — but even so, reducing unnecessary churn (stale project cleanup, less frequent cron +polling) is low-risk and worth doing regardless. + +### 3. Jobtracker-specific deploy process has no resource governance or real health checks — **HIGH confidence** (contributing to the app-level symptoms, not the host-wide ones) +**Why it's likely:** Independently true regardless of the systemd finding — see Phase 1 Findings 1, 2, 4 below. +Even once the D-Bus issue is fixed, these gaps mean a jobtracker-specific deploy still has less protection than +it should against any *other* future host issue. +**Supporting evidence:** Findings 1, 2, 4 (Phase 1 section). +**Alternative explanations:** None — structural facts about this repo's own deploy config, independent of the +host's health. + +### 4. AI service (local model / bundled Ollama) memory baseline — **LOW-MEDIUM confidence, downgraded** +**Why it's likely:** Still a real, undetermined baseline (Finding 5), but live data shows the host isn't +actually memory-constrained overall, so this matters less than Phase 1 assumed. +**Supporting evidence:** Finding 5. +**Alternative explanations:** Could be fully neutralized already if `AI_PROVIDER=gemini`/`groq` is set in prod +— still unconfirmed (would need to read `/opt/job-tracker/shared/.env`, a secrets file, not read during this +investigation on purpose). + +### 5. External database/reverse-proxy failure — **LOW confidence, still genuinely unknown** +**Why it's possibly relevant:** These are required, external, and entirely unobserved from this repo +(Finding 6) — Phase 2's server access didn't specifically target them either (the investigation focused on the +host-wide systemd finding once it emerged as the stronger lead). Not ruled out, just not the focus here. +**Supporting evidence:** Finding 6 (absence of evidence, not evidence of absence). +**Alternative explanations:** Downgraded from Phase 1's "could equally be the primary cause" — the systemd +D-Bus finding is now a more direct, evidenced explanation, but this is still worth checking (see remediation +plan step 5). + +--- + +## What I could not verify (needs root/server access) + +This Phase 2 session connected as the unprivileged `pi` user; `sudo` requires a password not available +non-interactively over this SSH session, so root-level commands below were not run. These are the highest-value +next steps, all read-only: + +1. **`sudo journalctl -u systemd-logind -u dbus -u dbus-broker --since "1 hour ago"`** (or `-u init.scope`) — the D-Bus broker's *own* logs, to find out why it's timing out (crash-and-respawn loop? resource exhaustion? a specific error at the moment of failure?). This is the single highest-value next command — it would very likely name the exact bug/resource involved. +2. **`sudo dmesg -T | grep -iE "oom|killed process|dbus|systemd"`** — kernel-level log; rules in/out an OOM or hardware event coinciding with the D-Bus failures, and catches anything below systemd's own visibility. +3. **`systemctl status dbus.service dbus-broker.service systemd-logind.service --no-pager`** (root) — current state/restart count of the D-Bus broker itself. +4. **`sudo journalctl --since "2026-04-29 23:00" --until "2026-04-29 23:30" --no-pager`** — full system log around the *first* occurrence, to see if something changed on the host that day (an update, a config change, a specific event) that could have introduced this. +5. Confirm whether `/opt/job-tracker/app` and the stale `/home/pi/jobtracker/JobTracker/` project are both still wired to receive deploys, or whether the latter is genuinely dead and safe to remove (removing it is a *fix*, out of scope for this investigation, but confirming its status is read-only). +6. Contents of `/opt/job-tracker/shared/.env` (specifically `AI_PROVIDER`) — resolves Finding 5. **Handle as a secret; do not paste it into any shared report or chat.** +7. `docker compose logs --tail=500` for the external reverse-proxy / MariaDB stacks (Finding 6) — still an open gap. +8. Whether `deunhealth`'s 389 restarts share a common trigger (check `docker inspect deunhealth --format '{{.State.Health}}'` and whatever container(s) it's configured to watch) — is it crash-looping *because of* the D-Bus issue, or is something else making it (and by extension the containers it watches) unhealthy in the first place? + +--- + +## Recommended Fixes + +*(Not implemented — investigation only, per instructions.)* + +| # | Fix | Effort | Risk | Expected impact | +|---|---|---|---|---| +| 0 | Root-cause the systemd D-Bus/session-bus degradation itself — start with `sudo journalctl -u dbus -u systemd-logind` and `sudo dmesg` around a live failure window (see "What I could not verify" #1-2) | Low (diagnosis) to Medium (fix, depends what's found — could be a systemd/dbus-broker package update, a resource limit like `DefaultTasksMax`, or a kernel issue) | Depends on what's found | **Highest** — this is the confirmed, live, host-wide root cause; every other fix in this table is secondary to actually understanding and fixing this one | +| 0b | Reduce unnecessary container/deploy churn: remove the dead `/home/pi/jobtracker/JobTracker/` project, reconcile the apparent `docker` vs `mediaserver` stack duplication, investigate why `deunhealth` has restarted 389+ times, and consider whether two separate per-minute cron autodeploys are necessary at that frequency | Low-Medium (mostly cleanup + one investigation) | Low (removing a confirmed-dead project), Medium (touching the live media stacks — verify which is actually in use before removing either) | Medium-High — reduces load on the already-struggling D-Bus mechanism while #0 is being root-caused | +| 1 | Add `mem_limit`/`cpus` (or `deploy.resources.limits`) to every service in `docker-compose.yml`, sized to the actual host specs (now known: 31GB RAM, 6 cores) | Low (config only) | Low | Medium — good practice regardless, but less urgent than Phase 1 assumed now that the host isn't actually memory-constrained | +| 2 | Add a proper `HEALTHCHECK` to `backend` (e.g. `curl`/`wget` against a lightweight `/health` or `/api/auth/config` endpoint) and `frontend`, and change `deploy.sh`'s post-deploy gate to check `.State.Health.Status` the same way it already does for `ai-service` | Low-Medium | Low | High — closes Finding 2, makes "hung but running" detectable and gates deploys on it | +| 3 | Move the image build off the production host entirely — build in CI (the Gitea runner already builds/tests everything else) and push to a registry; `deploy.sh` then only pulls + recreates, never compiles | Medium (CI/registry setup) | Low-Medium (new moving part: a registry) | High — removes Finding 4's concurrent build-vs-serve resource spike entirely, the single biggest structural risk found | +| 4 | Confirm and, if needed, set `AI_PROVIDER=gemini`/`groq` in the production `.env`, and confirm the `bundled-ollama` Compose profile is not active unless intentionally needed | Low (config only, once confirmed) | Low | Medium — removes a large, constant RAM baseline if it's currently active locally | +| 5 | Add proactive `docker image prune` / `docker builder prune` on a schedule (not just the failure-retry path) | Low | Low | Medium — prevents slow disk creep between incidents | +| 6 | Add `proxy_read_timeout`/`proxy_connect_timeout` tuning to `nginx.conf`'s `/api/` block, sized to the AI-backed endpoints' realistic worst case | Low | Low | Low-Medium — smooths one symptom, doesn't address root cause | +| 7 | Get visibility into the external MariaDB and reverse-proxy stacks (at minimum: their `docker-compose.yml`, health checks, and log access) | Low (access/documentation only) | None | High — closes Finding 6, the largest blind spot in this investigation | +| 8 | Consider a lock/guard so a second deploy can't start while one is still in-flight, and/or serialize the CI runner's own workload if it shares the host | Low-Medium | Low | Medium — prevents overlapping deploys from compounding the resource spike in Finding 4 | + +--- + +## Step-by-Step Remediation Plan (priority order) + +1. **Get root-level D-Bus/logind logs during or right after a live failure** (gap #1: `sudo journalctl -u dbus + -u systemd-logind -u docker --since "1 hour ago"`, gap #2: `sudo dmesg -T`). This is now the single + highest-value next action — it directly targets the confirmed root cause instead of a hypothesis, and would + very likely name the exact bug or exhausted resource within minutes. +2. **Check the D-Bus broker's own restart history** (`systemctl status dbus.service dbus-broker.service + systemd-logind.service`, root) — has it crashed and restarted itself repeatedly? That alone would explain + the intermittent-not-constant pattern observed. +3. **Investigate the `deunhealth` 389-restart loop specifically** (gap #8) — is it the *cause* of D-Bus churn, + a *victim* of it, or unrelated? Check what container(s) it watches and why they're (or it is) unhealthy. +4. **Clean up confirmed-dead/duplicate state** (fix #0b): the stale `/home/pi/jobtracker/JobTracker/` Compose + project, and reconcile whichever of `docker`/`mediaserver` is the real active media stack vs. an old + duplicate — reduces unnecessary churn while step 1-2 are investigated. Low-risk for the confirmed-dead + project; verify carefully before touching either live media stack. +5. **Consider whether the two per-minute cron autodeploys need to run that frequently** — every 1-2 minutes is + aggressive for auto-deploy polling and adds constant container churn; even relaxing to every 5 minutes + meaningfully reduces load on the struggling D-Bus mechanism with no real functional cost. +6. Once 1-3 identify the actual systemd/D-Bus root cause: **apply the appropriate fix** (likely one of: a + systemd/dbus-broker package update, adjusting a systemd resource limit such as `DefaultTasksMax` or an fd/ + inotify limit, or a kernel update) — the exact fix depends entirely on what step 1 finds, which is why it's + sequenced first. +7. **Independently, and regardless of the systemd finding:** add real health checks to jobtracker's `backend`/ + `frontend` (fix #2) and per-container resource limits (fix #1) — good practice, safe to do in parallel with + 1-6, not blocked by them. +8. **Get read access to the external reverse-proxy and MariaDB stacks** (Finding 6) — still an open gap, + lower priority now than the confirmed systemd finding but worth closing for completeness. +9. **Monitor** `journalctl -u dbus -u systemd-logind` for recurrence over the following week after step 6 lands, + before considering the incident closed — this class of intermittent issue (present since April per the log) + needs sustained observation, not a single clean hour, to confirm it's actually fixed. +10. **Validate** by watching a live deploy and a container crash/restart scenario post-fix, confirming both + complete cleanly without hitting the D-Bus timeout. + +--- + +## Quick Wins + +Low effort, likely to help immediately, safe to do without deeper investigation first: + +- Diagnostic steps 1-3 above (root `journalctl`/`dmesg`, D-Bus broker status, `deunhealth` investigation) — cost + nothing, read-only, and are now the single highest-value next actions given the confirmed live evidence. +- Removing the confirmed-dead `/home/pi/jobtracker/JobTracker/` Compose project — zero functional risk (it's + already `dead(1)`, not serving anything), reduces one small piece of the churn. +- Relaxing the two per-minute cron autodeploy schedules to something less aggressive (e.g. every 5 minutes) — + a one-line change, no functional downside, reduces constant container churn. +- Fix #2 (health checks on `backend`/`frontend`) — small, well-understood change, still worth doing regardless + of the systemd root cause. +- Fix #6 (nginx timeout tuning) — a few lines, no behavioral risk. + +--- + +## Long-Term Improvements + +- **Monitoring & alerting:** none observed anywhere in this repo or the deploy pipeline. At minimum, host-level + memory/disk/CPU alerting (even a simple cron + webhook) would have caught this class of issue before it + reached "server unresponsive." +- **Centralized logging:** container logs currently only exist via `docker compose logs` on the host itself and + are lost on container recreation unless captured. Shipping logs off-host (even to a simple log file rotated + and retained) would preserve evidence across incidents like this one. +- **Resource limits as standard practice:** every service should ship with `mem_limit`/`cpus` from day one going + forward, not retrofitted only after an incident. +- **Health checks as standard practice:** every service that serves traffic should have one; `ai-service`/ + `ollama` already show the team knows this pattern, it just wasn't applied to `backend`/`frontend`. +- **CI-built images + registry:** decouples "build" resource cost from "serve" resource availability entirely — + the single biggest structural improvement available (fix #3 above). +- **Backups:** not in scope of this investigation (no evidence reviewed either way), but worth confirming + separately given `jobtracker_data` is a named volume with no visible backup step in `deploy.sh`. +- **Deployment strategy:** consider blue-green or at least a "build first, verify, then cut over" pattern rather + than `--force-recreate` in place — reduces the window where a broken new container replaces a working old one + with no rollback path. +- **Container management:** a simple `docker system prune` schedule plus disk-usage alerting closes Finding 7 + permanently rather than reactively. +- **Host consolidation review:** this one box runs at least 7 unrelated Compose projects (jobtracker, a media + stack under at least two different names, inboxintel, mealie, resumesite) plus a self-hosted Gitea Actions + runner, with per-minute cron autodeploys for two of them. Worth a deliberate review of whether all of this + belongs on one host, and whether the CI runner specifically should be isolated from the boxes it deploys to + (a CI job's own container churn shouldn't be able to affect production media/app containers on the same + D-Bus/systemd instance). +- **systemd/D-Bus health monitoring specifically:** given this issue has been intermittently present since + April, a simple periodic check (e.g. `systemctl is-system-running` or a canary `systemd-run` test) with + alerting would have surfaced this months before it escalated to "server unresponsive." + +--- + +## Working Rules Confirmed + +No services were restarted. No configuration was modified. No containers, images, volumes, or networks were +deleted or pruned. No packages were installed. No fixes were applied. Every command run over SSH against +`pi@192.168.50.13` (Phase 2) was read-only: `journalctl`, `docker ps`/`inspect`/`compose ls`, `free`, `df`, +`uptime`, `last reboot`, `crontab -l`, `cat /etc/fstab`, `ps`. Several `sudo`-prefixed commands were attempted +and failed (no password available non-interactively) — those failures are themselves reported as an open gap, +not worked around. This report and the git history/config files/live logs read to produce it are the only +outputs of this investigation. diff --git a/docs/jobbjakt-cleanup-tracker.md b/docs/_archive/jobbjakt-cleanup-tracker.md similarity index 100% rename from docs/jobbjakt-cleanup-tracker.md rename to docs/_archive/jobbjakt-cleanup-tracker.md diff --git a/docs/jobbjakt-next-session.md b/docs/_archive/jobbjakt-next-session.md similarity index 100% rename from docs/jobbjakt-next-session.md rename to docs/_archive/jobbjakt-next-session.md diff --git a/docs/performance/MEMORY_LEAK_REPORT.md b/docs/_archive/performance/MEMORY_LEAK_REPORT.md similarity index 100% rename from docs/performance/MEMORY_LEAK_REPORT.md rename to docs/_archive/performance/MEMORY_LEAK_REPORT.md diff --git a/docs/performance/PERFORMANCE_IMPROVEMENTS.md b/docs/_archive/performance/PERFORMANCE_IMPROVEMENTS.md similarity index 100% rename from docs/performance/PERFORMANCE_IMPROVEMENTS.md rename to docs/_archive/performance/PERFORMANCE_IMPROVEMENTS.md diff --git a/docs/performance/ROOT_CAUSE_ANALYSIS.md b/docs/_archive/performance/ROOT_CAUSE_ANALYSIS.md similarity index 100% rename from docs/performance/ROOT_CAUSE_ANALYSIS.md rename to docs/_archive/performance/ROOT_CAUSE_ANALYSIS.md diff --git a/docs/s06-acceptance-run.md b/docs/_archive/s06-acceptance-run.md similarity index 100% rename from docs/s06-acceptance-run.md rename to docs/_archive/s06-acceptance-run.md diff --git a/docs/s07-uat.md b/docs/_archive/s07-uat.md similarity index 100% rename from docs/s07-uat.md rename to docs/_archive/s07-uat.md diff --git a/docs/security-assessments/M013-adversarial-security-assessment.md b/docs/_archive/security-assessments/M013-adversarial-security-assessment.md similarity index 100% rename from docs/security-assessments/M013-adversarial-security-assessment.md rename to docs/_archive/security-assessments/M013-adversarial-security-assessment.md diff --git a/docs/security-assessments/M014-security-remediation-verification.md b/docs/_archive/security-assessments/M014-security-remediation-verification.md similarity index 100% rename from docs/security-assessments/M014-security-remediation-verification.md rename to docs/_archive/security-assessments/M014-security-remediation-verification.md diff --git a/docs/security-assessments/M015-authorization-replay-report.md b/docs/_archive/security-assessments/M015-authorization-replay-report.md similarity index 100% rename from docs/security-assessments/M015-authorization-replay-report.md rename to docs/_archive/security-assessments/M015-authorization-replay-report.md diff --git a/docs/security-assessments/M015-hostile-fixture-setup.json b/docs/_archive/security-assessments/M015-hostile-fixture-setup.json similarity index 100% rename from docs/security-assessments/M015-hostile-fixture-setup.json rename to docs/_archive/security-assessments/M015-hostile-fixture-setup.json diff --git a/docs/security-assessments/M015-hostile-fixture-setup.md b/docs/_archive/security-assessments/M015-hostile-fixture-setup.md similarity index 100% rename from docs/security-assessments/M015-hostile-fixture-setup.md rename to docs/_archive/security-assessments/M015-hostile-fixture-setup.md diff --git a/docs/security-assessments/M015-s02-probe-results.json b/docs/_archive/security-assessments/M015-s02-probe-results.json similarity index 100% rename from docs/security-assessments/M015-s02-probe-results.json rename to docs/_archive/security-assessments/M015-s02-probe-results.json diff --git a/docs/ai/ai-principles.md b/docs/ai/ai-principles.md new file mode 100644 index 0000000..e5e3802 --- /dev/null +++ b/docs/ai/ai-principles.md @@ -0,0 +1,55 @@ +# AI Principles + +## 1. User First + +The user owns all generated content. + +--- + +## 2. Never Invent Facts + +AI must not create: + +- Jobs. +- Companies. +- Qualifications. +- Skills. +- Experience. + +--- + +## 3. Explain Changes + +Where possible show: + +Before + +↓ + +After + +--- + +## 4. Preserve Context + +AI should understand: + +- User career. +- Target job. +- Industry. + +--- + +## 5. Allow Manual Editing + +Every AI output must be editable. + +--- + +## 6. Fail Safely + +If AI fails: + +- Show error. +- Allow retry. +- Do not lose user data. \ No newline at end of file diff --git a/docs/ai/cost-control.md b/docs/ai/cost-control.md new file mode 100644 index 0000000..e86c40d --- /dev/null +++ b/docs/ai/cost-control.md @@ -0,0 +1,39 @@ +# AI Cost Control + +## Purpose + +Prevent uncontrolled AI expenses. + +--- + +# Requirements + +Track: + +- User requests. +- Tokens. +- Provider usage. + +--- + +# Future SaaS + +Support: + +Free: + +Limited usage. + +Premium: + +Higher limits. + +--- + +# Optimisation + +Prefer: + +- Smaller models for simple tasks. +- Caching where appropriate. +- Reusing generated results. \ No newline at end of file diff --git a/docs/ai/cover-letter-ai.md b/docs/ai/cover-letter-ai.md new file mode 100644 index 0000000..aee24f5 --- /dev/null +++ b/docs/ai/cover-letter-ai.md @@ -0,0 +1,49 @@ +# Cover Letter AI + +## Purpose + +Generate personalised cover letters. + +--- + +# Inputs + +Use: + +- Career Profile. +- Master CV. +- Job description. + +--- + +# Features + +Generate: + +- First draft. +- Improvements. +- Tone changes. + +--- + +# User Control + +Allow: + +- Editing. +- Regeneration. +- Manual writing. + +--- + +# Rules + +Avoid generic letters. + +Focus on: + +Why this person. + +Why this company. + +Why this role. \ No newline at end of file diff --git a/docs/ai/cv-ai.md b/docs/ai/cv-ai.md new file mode 100644 index 0000000..52a7d17 --- /dev/null +++ b/docs/ai/cv-ai.md @@ -0,0 +1,58 @@ +# CV AI Features + +## Purpose + +Improve CV creation. + +--- + +# Features + +## Improve Writing + +Example: + +Original: + +"Worked on websites" + +AI: + +"Developed and maintained customer-facing web applications." + +--- + +## Suggest Content + +AI can suggest: + +- Missing skills. +- Better wording. + +--- + +## Rewrite + +Options: + +- More professional. +- More concise. +- More technical. + +--- + +## Generate + +Create: + +- Summary. +- Bullet points. +- Sections. + +--- + +# Rules + +AI suggestions require approval. + +Never overwrite original content. \ No newline at end of file diff --git a/docs/ai/future-ai.md b/docs/ai/future-ai.md new file mode 100644 index 0000000..11d5858 --- /dev/null +++ b/docs/ai/future-ai.md @@ -0,0 +1,49 @@ +# Future AI Features + +Possible future features: + +--- + +## AI Career Assistant + +A contextual assistant aware of: + +- Career history. +- Goals. +- Applications. + +--- + +## Interview Coach + +Generate: + +- Questions. +- Answers. +- Feedback. + +--- + +## Career Recommendations + +Suggest: + +- Skills. +- Roles. +- Learning paths. + +--- + +## Automatic Application Assistant + +Potential: + +- Find relevant jobs. +- Prepare documents. +- Track progress. + +--- + +# Priority + +Only build after core workflows are excellent. \ No newline at end of file diff --git a/docs/ai/jobs-ai.md b/docs/ai/jobs-ai.md new file mode 100644 index 0000000..6e83daf --- /dev/null +++ b/docs/ai/jobs-ai.md @@ -0,0 +1,48 @@ +# Job AI Features + +## Purpose + +Help users understand opportunities. + +--- + +# Features + +## Job Analysis + +Extract: + +- Required skills. +- Responsibilities. +- Seniority. +- Keywords. + +--- + +## Matching + +Compare: + +Job + +against + +Career Profile + +--- + +Output: + +- Strengths. +- Gaps. +- Suggestions. + +--- + +## Application Assistance + +Suggest: + +- CV improvements. +- Cover letter topics. +- Interview preparation. \ No newline at end of file diff --git a/docs/ai/model-selection.md b/docs/ai/model-selection.md new file mode 100644 index 0000000..3e56616 --- /dev/null +++ b/docs/ai/model-selection.md @@ -0,0 +1,60 @@ +# AI Model Selection + +## Goal + +Use appropriate models for tasks. + +--- + +# Task Categories + +## Simple Tasks + +Examples: + +- Grammar correction. +- Shortening text. + +Use: + +Fast/cheap models. + +--- + +## Complex Tasks + +Examples: + +- CV generation. +- Career analysis. + +Use: + +Higher quality models. + +--- + +## Long Context Tasks + +Examples: + +- Analysing full CV. +- Comparing job descriptions. + +Use: + +Large context models. + +--- + +# Principles + +Do not use expensive models unnecessarily. + +Balance: + +Quality. + +Speed. + +Cost. \ No newline at end of file diff --git a/docs/ai/overview.md b/docs/ai/overview.md new file mode 100644 index 0000000..e57ddb3 --- /dev/null +++ b/docs/ai/overview.md @@ -0,0 +1,72 @@ +# AI System Overview + +## Purpose + +AI enhances Jobjakt workflows. + +It helps users: + +- Create better applications. +- Improve documents. +- Understand opportunities. +- Prepare for interviews. + +AI does not replace user decisions. + +--- + +# AI Philosophy + +Jobjakt is not an AI chatbot. + +AI should appear where it provides clear value. + +Examples: + +Good: + +"Improve this CV bullet point" + +Good: + +"Tailor this CV for this job" + +Good: + +"Suggest interview questions" + +Bad: + +Empty chat window with no context. + +--- + +# AI Sources + +AI can use: + +- Career Profile. +- Master CV. +- Job descriptions. +- User-provided documents. +- Application history. + +--- + +# User Control + +AI output must: + +- Be visible. +- Be editable. +- Require approval before saving. + +--- + +# Important Rule + +Never silently modify: + +- Career profile. +- Master CV. +- Application documents. \ No newline at end of file diff --git a/docs/ai/privacy.md b/docs/ai/privacy.md new file mode 100644 index 0000000..24d76d7 --- /dev/null +++ b/docs/ai/privacy.md @@ -0,0 +1,37 @@ +# AI Privacy + +## Principles + +Users control their data. + +--- + +# External AI Providers + +Before sending data: + +Clearly communicate: + +- What is sent. +- Why it is needed. +- Provider used. + +--- + +# Future SaaS + +Support: + +- User consent. +- Data deletion. +- Export. + +--- + +# Sensitive Information + +Be careful with: + +- Personal details. +- Employment history. +- Documents. \ No newline at end of file diff --git a/docs/ai/prompts.md b/docs/ai/prompts.md new file mode 100644 index 0000000..8b4bfeb --- /dev/null +++ b/docs/ai/prompts.md @@ -0,0 +1,51 @@ +# AI Prompt Management + +## Purpose + +Centralise prompts. + +Avoid random prompts scattered throughout code. + +--- + +# Requirements + +Prompts should: + +- Be version controlled. +- Be documented. +- Have tests where possible. + +--- + +# Prompt Structure + +Every prompt should define: + +Purpose. + +Inputs. + +Expected output. + +Restrictions. + +--- + +# Example + +Task: + +Improve CV bullet point. + +Input: + +Existing bullet. + +Output: + +Improved bullet. + +Restrictions: + +Do not add unsupported achievements. \ No newline at end of file diff --git a/docs/ai/providers.md b/docs/ai/providers.md new file mode 100644 index 0000000..f1ffb64 --- /dev/null +++ b/docs/ai/providers.md @@ -0,0 +1,66 @@ +# AI Providers + +## Purpose + +Support multiple AI providers. + +--- + +# Current/Future Providers + +Possible: + +- OpenAI. +- Anthropic. +- Google Gemini. +- Groq. +- Ollama. +- Other compatible APIs. + +--- + +# Provider Architecture + +Use abstraction: + +AI Provider Interface + +↓ + +Provider Implementation + +↓ + +Model + +--- + +# Requirements + +Users should eventually be able to: + +- Select provider. +- Select model. +- Configure API keys. + +--- + +# Admin Controls + +Admins should control: + +- Available providers. +- Allowed models. +- Usage limits. + +--- + +# SaaS Consideration + +Different plans may have: + +Free models. + +Premium models. + +BYO-key support. \ No newline at end of file diff --git a/docs/ai/safety.md b/docs/ai/safety.md new file mode 100644 index 0000000..eb6fb12 --- /dev/null +++ b/docs/ai/safety.md @@ -0,0 +1,3 @@ +# safety + +TODO: Complete documentation. diff --git a/docs/ai/security.md b/docs/ai/security.md new file mode 100644 index 0000000..83ecf10 --- /dev/null +++ b/docs/ai/security.md @@ -0,0 +1,44 @@ +# AI Security + +## Purpose + +Protect user data. + +--- + +# Prompt Injection Protection + +Job descriptions and uploaded documents are untrusted input. + +Never allow external text to override system instructions. + +--- + +# Data Protection + +Protect: + +- CV data. +- Personal information. +- Documents. + +--- + +# Validation + +Validate: + +- AI outputs. +- Generated files. +- User inputs. + +--- + +# File Processing + +Uploaded files may contain: + +- Malicious content. +- Hidden instructions. + +Treat all files as untrusted. \ No newline at end of file diff --git a/docs/application-discovery-report.md b/docs/application-discovery-report.md new file mode 100644 index 0000000..ef65e33 --- /dev/null +++ b/docs/application-discovery-report.md @@ -0,0 +1,638 @@ +# 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**: + +1. **There is no Job entity.** The only entity is `JobApplication`, and the pipeline's first stage is `Applied` (`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 in `docs/MASTER_IMPLEMENTATION_GUIDE.md`. +2. **The CV Builder does not exist.** `CareerWorkspacePage` renders a "CV Builder" tab that passes a `careerView` prop to `ProfilePage` — 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. +3. **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.md` opens with "TODO: Complete documentation." The four ADRs in `docs/_archive/decisions/` are **0 bytes**. Meanwhile `docs/_archive/SYSTEM_OVERVIEW.md` (20 KB) and `docs/_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 `/register` route** in the router. Sign-up is folded into `LoginPage.tsx`. +- **Login** — solid: email/password, Google, Microsoft, 2FA challenge, trusted devices. +- **First setup** — `OnboardingChecklist.tsx`: two items (upload CV, add a job), dismissible to `localStorage`, 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-email` exists; 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 `profileCvText` being 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. +- `/profile` and `/career` both render `ProfilePage`, differing only by a `careerOnly` boolean. 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 commit `aa3567d`. +- **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 `Status` canonicalized by `JobPipeline.Normalize`, preserving unknown values (a good, non-destructive design). +- **Follow-ups** — `FollowUpReminderHostedService`, `RulesEngine` (auto-ghosting), AI drafts, send. +- **Communication** — `Correspondence` per 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: + +1. **Fatal: the destination stage does not exist.** The wizard walks the user through preparing an application, then must save it as `Applied`. There is no `Saved` / `Interested` / `Preparing` stage (`JobPipeline.cs:24-31` — stages are `Applied, 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. +2. **"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. +3. **Import is preview-only.** `JobImportController` exposes 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) → `StructuredCvProfile` JSON → applied to `ApplicationUser.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. The `StructuredCvProfile` shape covers Summary, Jobs, Education, Certifications, Projects, Skills, Languages, Interests, OtherSections — the guide additionally names **Achievements/Awards, Publications, Organisations, References**, which have no home beyond generic `OtherSections`. +- **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.** `TailoredCvDraft` is 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 ``. 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 | + |---|---| + | `JobApplicationsController` | 2313 | + | `ProfileCvController` | 2249 | + | `GmailController` | 1023 | + | `AuthController` | 879 | + + `JobApplicationsController` alone 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 → `google` handler, else → `local` JWT (symmetric key, issuer/audience validated). Cookie sessions (`jobtracker_auth`, HttpOnly, SameSite=Lax) with **CSRF double-submit** enforcement on mutating requests. `Auth:Require=true` in 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 var** `AI_PROVIDER` ∈ {`ollama`, `gemini`, `groq`}. + + > **The documented AI architecture does not exist.** `docs/00-ai-context.md` describes 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-side `react-router-dom` v6 app. `App.tsx` uses **both** `createBrowserRouter` (public routes) **and** a nested `` inside a catch-all `Shell` (authenticated routes). Meanwhile `react-scripts` 5.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` + `axios` per component, with a hand-rolled `refreshToken` counter 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 custom `theme.ts` (439 lines), light/dark. Consistent, but design tokens live in component-level `sx` props (e.g. the identical `boxShadow: "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:** +- **`JobApplication` conflates 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 what `Attachments` already says; there is a dedicated `AttachmentFlagsRecomputeTests` suite to keep them honest — a test that exists only because of the duplication. +- **`Tags` is a JSON array in a string column** — not queryable; `/tags` and `/tag-trends` must scan. +- **CV text stored three times**: `CvExtractionRun.RawExtractedText`, `.NormalizedText`, `.StructuredProfileJson`, plus `ApplicationUser.ProfileCvText` and `.ProfileCvStructureJson`. Deliberate (audit trail), but unbounded — no retention policy on extraction runs. +- **`AvatarImageDataUrl`** — base64 image in a DB column on the hot `/auth/me` path. +- **`Status` is 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 via `bundled-ollama` profile, GPU). External `jobtracker_shared` network. +- **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 ci` SIGSEGV, build OOM). **There is no GitHub Actions workflow** — do not look in `.github/`. +- **Environment variables:** extensive, `.env` correctly gitignored, `.env.example` present. +- **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 +1. `ProfilePage` — split profile / career / security. +2. `CareerWorkspacePage` — currently a facade. +3. Onboarding — checklist → guided flow. +4. `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`, with `Download` always 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) + +1. **The theme system must become data, not code.** This is the single highest-leverage lesson. `CvTemplateRenderer` renders 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 a `CvTheme` document (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. +2. **`Overview | Content | Customize | Preview/Export` is the right tab spine** — and it maps onto Jobjakt's stated `Content Tab → Customise Tab → Preview → Export`. +3. **Live preview beside the editor**, not behind a request. +4. **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 `main` and of `docs/_archive/`, which is all this audit searched. It is not true of the repository. +> +> A **`feature/career-workspace` branch exists** (local *and* on `origin`, 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/`, never `git 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:** +1. `views/CareerWorkspacePage.tsx:25` renders ``. +2. `:31` passes `careerView={tab}` into ``. +3. `views/ProfilePage.tsx:231` destructures `careerView = "master"`, and `:235` types 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. +4. 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:** + +1. **`JobApplication` conflates Job and Application** — blocks the primary workflow. Everything else on this list is cosmetic by comparison. +2. **God controllers** — `JobApplicationsController` 2313 / 38 endpoints; `ProfileCvController` 2249; `GmailController` 1023; `AuthController` 879; `StartupInitializationExtensions` 1356. The guide's own "Coding Philosophy" forbids exactly this. +3. **Dead `careerView` prop** — a shipped no-op tab. +4. **Hardcoded CV templates** — dead-end for the entire Phase 4. +5. **Three frontend stacks** — Next.js App Router shell + react-router v6 (×2 patterns: `createBrowserRouter` *and* nested ``) + react-scripts as test runner. Pays three toolchain costs for one CSR app; causes the known dev-only deep-link 404. +6. **No frontend data layer** — no server-cache library; hand-rolled `refreshToken` counters threaded via props. Root cause of the 600–1400-line components. +7. **`ProfilePage` at 1368 lines** forked by a boolean, serving two routes. +8. **Denormalized `Has*` flags** — kept honest only by a dedicated test suite. +9. **`Tags` as a JSON string column** — unqueryable; forces scans in `/tags`, `/tag-trends`. +10. **Unbounded CV text/artifact storage** — no retention on `CvExtractionRun`; three copies of every CV. +11. **`AvatarImageDataUrl`** — base64 blob in a DB column on the `/auth/me` hot path. +12. **Only 10 migrations + a `SyncModelSnapshot`** — model drifted from migrations, reconciled in bulk. +13. **`JobTrackerBackend` link-compilation** — source lives in one place, compiles from another. Self-described as "transitional". Surprising to every new contributor and every tool. +14. **`Status` free-text at the DB level** — canonicalization is application-only. +15. **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 by `JobApplicationsAuthorizationTests`, `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:** `.env` gitignored; DP keys and runtime exports untracked in `519c32e`. + +### 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): +1. `JobTrackerBackend` link-compilation exists so tests can reach controllers without the web host — deliberate, self-described "transitional". +2. `Status` is free-text and canonicalized in the application layer specifically so custom user values are never destroyed (`JobPipeline.cs` docstring). Good decision; undocumented outside the source file. +3. 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. +4. Ollama is intentionally **not** bundled by default (`bundled-ollama` profile) so deploys reuse a shared instance. +5. `AI_PROVIDER=gemini` exists specifically "to offload a weak local GPU" in prod (compose comment). + +--- + +## 14. Recommended roadmap + +Full task breakdown in `docs/implementation-roadmap.md`. Ordering rationale: + +1. **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. +2. **Phase 2 — UX.** Onboarding + the Profile/Career split. Cheap, high-visibility. +3. **Phase 3 — Career Workspace.** Requires the profile model to be real. +4. **Phase 4 — CV Builder.** Requires Phase 3 + a data-driven theme system. **The largest single piece of work in the plan.** +5. **Phase 5 — AI.** Mostly polish; the hard part is done. +6. **Phase 6 — Job Discovery.** Greenfield; explicitly an enhancement. +7. **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`, and `docs/_archive/`. **It never ran `git branch -a`.** That missed `feature/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: + +- `careerView` dead: `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 -c` across `docs/**/*.md`. + +Not verified (out of read-only scope): mockups on `F:\`, dependency CVEs, production runtime configuration. diff --git a/docs/architecture/ai-architecture.md b/docs/architecture/ai-architecture.md new file mode 100644 index 0000000..6d13b71 --- /dev/null +++ b/docs/architecture/ai-architecture.md @@ -0,0 +1,56 @@ +# AI Architecture + +## Purpose + +Centralise AI functionality. + +--- + +# AI Service Layer + +Applications should not directly call AI providers. + +Instead: + +Feature + +↓ + +AI Service + +↓ + +Provider + +↓ + +Model + +--- + +# Providers + +Support: + +- OpenAI. +- Gemini. +- Claude. +- Groq. +- Ollama. + +--- + +# Requirements + +Support: + +- Switching providers. +- Usage tracking. +- Error handling. +- Cost control. + +--- + +# Rules + +AI output requires validation. \ No newline at end of file diff --git a/docs/architecture/api-design.md b/docs/architecture/api-design.md new file mode 100644 index 0000000..794101f --- /dev/null +++ b/docs/architecture/api-design.md @@ -0,0 +1,39 @@ +# API Design + +## Principles + +APIs should be: + +- Predictable. +- Secure. +- Documented. + +--- + +# Requirements + +Endpoints should: + +- Validate input. +- Return clear errors. +- Use correct status codes. + +--- + +# Versioning + +Future SaaS may require: + +API versions. + +--- + +# Security + +Every endpoint must verify: + +Authentication. + +Authorization. + +Ownership. \ No newline at end of file diff --git a/docs/architecture/authentication.md b/docs/architecture/authentication.md new file mode 100644 index 0000000..c072915 --- /dev/null +++ b/docs/architecture/authentication.md @@ -0,0 +1,48 @@ +# Authentication Architecture + +## Supported Methods + +Email/password. + +Google OAuth. + +Microsoft OAuth. + + +--- + +# Future + +Potential: + +Other providers. + +Enterprise login. + +--- + +# Requirements + +Authentication should support: + +- Registration. +- Login. +- Password reset. +- 2FA. +- Account management. + +--- + +# User Creation + +OAuth users: + +If account does not exist: + +Create automatically. + +--- + +# Separation + +Authentication identity != career profile. \ No newline at end of file diff --git a/docs/architecture/backend.md b/docs/architecture/backend.md new file mode 100644 index 0000000..a9152c5 --- /dev/null +++ b/docs/architecture/backend.md @@ -0,0 +1,75 @@ +# Backend Architecture + +## Purpose + +Define backend organisation. + +--- + +# Preferred Structure + +Example: + +Application + +├── Features + +├── Domain + +├── Infrastructure + +├── Services + +├── API + +--- + +# Responsibilities + +## API Layer + +Handles: + +- HTTP requests. +- Authentication. +- Validation. +- Responses. + +Should not contain business logic. + +--- + +## Application Layer + +Handles: + +- Workflows. +- Business operations. +- Coordination. + +--- + +## Domain Layer + +Contains: + +- Entities. +- Rules. +- Business concepts. + +--- + +## Infrastructure + +Handles: + +- Database. +- External APIs. +- File storage. +- AI providers. + +--- + +# Rule + +Controllers should remain thin. \ No newline at end of file diff --git a/docs/architecture/background-jobs.md b/docs/architecture/background-jobs.md new file mode 100644 index 0000000..c0655f0 --- /dev/null +++ b/docs/architecture/background-jobs.md @@ -0,0 +1,36 @@ +# Background Jobs + +## Purpose + +Handle long-running operations. + +--- + +# Examples + +- CV extraction. +- AI generation. +- PDF rendering. +- Email syncing. + +--- + +# Requirements + +Jobs should: + +- Report status. +- Handle failures. +- Retry safely. + +--- + +# UX + +Users should see: + +Processing. + +Completed. + +Failed. \ No newline at end of file diff --git a/docs/architecture/career-architecture.md b/docs/architecture/career-architecture.md new file mode 100644 index 0000000..a9bce95 --- /dev/null +++ b/docs/architecture/career-architecture.md @@ -0,0 +1,55 @@ +# Career Architecture + +## Purpose + +Define Career Workspace structure. + +--- + +# Source Of Truth + +Career Profile. + +--- + +# Flow + +Career Profile + +↓ + +Master CV + +↓ + +Theme + +↓ + +Generated CV + +↓ + +Application Copy + +--- + +# Important + +Themes are presentation. + +They do not own content. + +--- + +# Multiple CVs + +Do not create: + +Multiple disconnected profiles. + +Instead: + +One career profile. + +Many outputs. \ No newline at end of file diff --git a/docs/architecture/current.md b/docs/architecture/current.md new file mode 100644 index 0000000..1723e3b --- /dev/null +++ b/docs/architecture/current.md @@ -0,0 +1,375 @@ +# Jobjakt — Current Architecture + +> **This document describes the system as it actually is.** Every claim was verified against code. +> Last verified: 2026-07-17 (Phase 0). Supersedes the archived `docs/_archive/SYSTEM_OVERVIEW.md` (2026-07-02). +> +> **Rule:** if this document and the code disagree, the code wins — and this document is a bug. Fix it. +> Do not trust other files under `docs/` over this one; most are stubs. + +--- + +## 1. What the product is + +Jobjakt is a self-hosted, multi-user job application tracking platform with local-AI career assistance: + +- Track jobs and applications end-to-end (pipeline stages, follow-ups, deadlines, salary, tags, notes). +- Company CRM (pipeline stage, contact dates, recruiter details) — company-level only, no people entities. +- Correspondence log per application: **Gmail OAuth import with a human review queue**, IMAP, Microsoft Graph. +- Attachments per application with purpose metadata and AI-inclusion toggles. +- CV platform: upload → OCR/extraction → structured parsing → per-job tailored CV drafts → templated PDF via Playwright. +- AI drafts: cover letters, recruiter messages, follow-up drafts, job summaries, match scoring, interview prep. +- Rules engine (auto-ghosting), reminder emails, daily JSON export, event trail, automated DB backup. +- Admin: user management, audit log, system readiness. +- Production: `https://jobs.cesnimda.uk` via Gitea Actions → SSH → Docker Compose. + +**Product hierarchy** (from `docs/MASTER_IMPLEMENTATION_GUIDE.md` — job tracking is the core; career tools support it): +Job Tracking → Applications → Workflow → Follow-ups → Communication, then Career Profile → Master CV → CV Builder → Cover Letters → Portfolio → Interview Prep, then Job Discovery. + +--- + +## 2. Architecture overview + +```mermaid +flowchart LR + subgraph Client + UI[React 19 SPA
MUI 7, react-router 6
Next.js 16 CSR shell] + end + + subgraph Frontend container + NGINX[nginx 1.29-alpine
serves static export + proxies /api] + end + + subgraph Backend container + API[ASP.NET Core net9.0
JobTrackerApi host] + BG[7 hosted services:
Rules, FollowUpReminder, DailyExport,
JobEnrichment, SummarizerProbe,
CvProcessing, DatabaseBackup] + DB[(SQLite default
or MariaDB/MySQL)] + FS[/Data root:
Attachments, CvArtifacts,
exports, DP keys/] + end + + subgraph AI stack + AISVC[FastAPI ai-service :8001
distilbart summarizer,
OCR, docx/pdf extraction] + PROV[Provider via AI_PROVIDER env:
ollama qwen2.5:7b / gemini / groq] + end + + EXT1[Google OAuth / Gmail API] + EXT2[Microsoft Graph / IMAP] + EXT3[Job sites: Finn, NAV,
LinkedIn, Jobbnorge] + EXT4[SMTP] + EXT5[LibreTranslate optional] + + UI --> NGINX --> API + API --> DB + API --> FS + API --> AISVC --> PROV + API --> EXT1 + API --> EXT2 + API --> EXT3 + API --> EXT4 + API --> EXT5 + 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 link-compiles, via ``, files physically located in `../Data`, `../Models`, `../JobTrackerApi/Controllers`, `../JobTrackerApi/Services`. Exists so tests can reference controllers/services without the web host. | +| `JobTrackerApi.Tests/` | xUnit, 36 test files incl. authorization + hostile-fixture suites. | +| `Models/`, `Data/` (repo root) | The *real* EF models and `JobTrackerContext`. | +| `job-tracker-ui/` | React SPA inside a Next.js shell. | +| `tools/summarizer/` | FastAPI AI service (own Dockerfile, pytest tests). | +| `tools/hostile-fixture-db/` | Security test fixture generator. | +| `deploy/`, `.gitea/workflows/` | Prod deploy script + CI/CD. | + +**Source lives in one place and compiles from another.** Any tool assuming csproj-adjacent source will mislead you. Check `JobTrackerBackend.csproj` before adding files or projects. + +> Corrected 2026-07-17: the dead root `Controller/` (singular) folder described in the archived overview **no longer exists** — removed in `519c32e`. + +--- + +## 3. Technology stack + +**Backend:** ASP.NET Core net9.0, EF Core 9 (SQLite default; Pomelo MySQL/MariaDB via `Database:Provider`), ASP.NET Identity Core, JWT bearer (smart policy scheme: local + Google), built-in RateLimiter, DataProtection (file-system keys), Playwright (PDF export). + +**Frontend:** **Next.js 16** + React 19 + **TypeScript 5.9** + MUI 7 (+ x-data-grid, x-date-pickers, lab), axios, react-router-dom 6, @tanstack/react-table, i18n EN + NB (custom provider), Jest/RTL. + +> Corrected 2026-07-17: the archived overview said "CRA/react-scripts 5, TypeScript 4.9". The CRA→Next.js migration has happened. See §4 for what that migration did and did not do. + +**AI:** FastAPI + transformers (`sshleifer/distilbart-cnn-12-6`) for summaries; pytesseract/PyMuPDF/pypdf/python-docx for extraction/OCR; **one** generation provider selected by the `AI_PROVIDER` env var ∈ {`ollama` (default, `qwen2.5:7b`), `gemini`, `groq`}; TTL cache. + +**Infra:** Docker Compose (backend, frontend/nginx 1.29-alpine, ai-service, ollama opt-in via `bundled-ollama` profile w/ GPU), Gitea Actions CI → SSH deploy → `deploy/deploy.sh`, external `jobtracker_shared` network. + +--- + +## 4. Frontend architecture + +**Three toolchains coexist. This is the single most confusing thing about the frontend.** + +1. **Next.js 16 App Router** (`app/layout.tsx`, `app/page.tsx`) — a thin shell that mounts a client-side app. The CRA→Next migration was a **CSR lift-and-shift**: no SSR, no server components, no Next routing, no data fetching. Next is effectively a build tool here. Static export → nginx. +2. **react-router-dom v6** — does the actual routing, in **two different patterns inside one file** (`src/App.tsx`): `createBrowserRouter` for public routes (`/`, `/login`, `/forgot-password`, `/reset-password`, `/verify-email`) and a nested `` inside a catch-all `Shell` for authenticated routes. +3. **react-scripts 5.0.1** — still a dependency, used **only** as the test runner (`"test": "react-scripts test"`). + +Known consequence: a **dev-only 404 on deep links** follows directly from the Next shell + client router combination. + +**Routes** (`src/App.tsx`): public — `/`, `/login`, `/forgot-password`, `/reset-password`, `/verify-email`. Authenticated — `/dashboard`, `/jobs`, `/reminders`, `/kanban`, `/companies`, `/correspondence`, `/correspondence/review`, `/profile`, `/career`, `/trash`, `/settings`, `/settings/connected-accounts`, `/admin/{audit,users,system}`. + +> **No `/register` route exists.** Sign-up is folded into `LoginPage.tsx`, and the endpoint is disabled by default (§5). + +**State management: none.** No Redux/Zustand/React Query. Local `useState` + `axios` per component, with a hand-rolled `refreshToken` counter threaded through props. Two workspace-cache hooks exist (`components/job-workspace/useWorkspaceTabCache.ts`, `useJobWorkspaceBaseData.ts`). This is the root cause of the oversized components below. + +**Styling:** MUI `sx` + custom `src/theme.ts` (439 lines), light/dark. Design tokens live inline in component `sx` props rather than in the theme (e.g. the same `boxShadow: "0px 8px 24px -12px rgba(15,23,42,0.12)"` is repeated across pages). There is no component primitives layer and no Storybook. + +**Oversized components** (refactor targets, in order): `JobDetailsDialog.tsx` (1400), `ProfilePage.tsx` (1368), `JobTable.tsx` (786), `Correspondence.tsx` (732), `DashboardView.tsx` (666), `AdminSystemPage.tsx` (623), `AddJobModal.tsx` (618). + +`ProfilePage.tsx` serves **both** `/profile` and `/career`, forked by a `careerOnly` boolean — two nav destinations rendering one component. + +--- + +## 5. Authentication & authorization + +- **Smart policy scheme:** inspects the bearer token issuer — Google ID tokens (`accounts.google.com`) → `google` handler (validated against `Auth:GoogleClientId`); everything else → `local` JWT (symmetric `Auth:JwtKey`, issuer/audience validated, 2-min clock skew). +- **Cookie sessions:** local handler also reads `jobtracker_auth` (HttpOnly, SameSite=Lax, Secure-configurable, 30d when persistent). **CSRF double-submit** middleware enforces cookie+header match on all mutating requests when a session cookie is present (login/register/reset/csrf 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 is required but unconfigured. +- 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/JobEvent/CV entities filter through their parent's owner. Covered by `JobApplicationsAuthorizationTests`, `OwnershipGuardTests`. +- Roles via Identity: admin-only `UsersController`, `AdminAuditController`, `AdminSystemController`. +- **2FA:** TOTP (`Otp.NET`), encrypted secrets, QR enrolment (`QRCoder`), recovery codes, trusted devices (`jobtracker_td` cookie), pending-token flow. +- **Sessions:** `UserSession` entity + `SessionsController` — list/revoke active sessions. +- Password policy: min 8, digit + lowercase. Reset via emailed token (SMTP required). +- **Registration is disabled by default** — `AuthController.cs:135` reads `Auth:AllowRegistration` defaulting to `false` and returns HTTP 403. There is no CAPTCHA anywhere. +- **Rate limiting (3 fixed-window policies):** `auth-login` 10/window, `auth-email` 5/window, `auth-2fa-challenge` 5/window. **AI and other expensive endpoints are unthrottled.** + +--- + +## 6. Database + +EF Core, **11 migrations**. App DbSets + Identity tables. + +```mermaid +erDiagram + ApplicationUser ||--o{ Company : owns + ApplicationUser ||--o{ Job : owns + ApplicationUser ||--o{ JobApplication : owns + ApplicationUser ||--o| UserRuleSettings : has + ApplicationUser ||--o{ GmailConnection : has + ApplicationUser ||--o{ CvUploadArtifact : owns + ApplicationUser ||--o{ CvExtractionRun : owns + ApplicationUser ||--o{ UserSession : has + ApplicationUser ||--o{ TrustedDevice : has + Company ||--o{ Job : "posts" + Company ||--o{ JobApplication : "has jobs" + Job ||--o{ JobApplication : "applied to via" + JobApplication ||--o{ Correspondence : messages + JobApplication ||--o{ Attachment : attachments + JobApplication ||--o{ JobEvent : events + JobApplication ||--o| TailoredCvDraft : "1:1 draft" + CvUploadArtifact ||--o{ CvExtractionRun : "source of" +``` + +**Entities:** `Company`, **`Job`**, `JobApplication`, `Correspondence`, `GmailConnection`, `GmailReviewDecision`, `MicrosoftGraphConnection`, `ImapConnection`, `Attachment`, `RuleSettings`, `UserRuleSettings`, `SystemEmailSettings`, `JobEvent`, `CvUploadArtifact`, `CvExtractionRun`, `TailoredCvDraft`, `TwoFactorRecoveryCode`, `TrustedDevice`, `UserSession`. + +**Key notes:** +- `ApplicationUser` (IdentityUser) also stores `ProfileCvText`, **`ProfileCvStructureJson`** (the master career profile — a JSON blob, not relational), `AvatarImageDataUrl` (base64 in a column, on the `/auth/me` hot path), Google/Microsoft link info, TOTP secrets, current CV artifact/run pointers. +- **`Job` vs `JobApplication`** — `Job` is the opportunity (title, company, description, URL, salary, location, deadline, tags); `JobApplication` is the user's pursuit of it (status, dates, follow-ups, correspondence, attachments). Introduced in Phase 0 as an **additive** step: `JobApplication.JobId` is a nullable FK and `JobApplication` still carries its original opportunity columns for backwards compatibility. See §16 and `docs/decisions/ADR-002-job-application-model.md`. +- Salary is **structured**: `SalaryMin`, `SalaryMax`, `SalaryCurrency`, `SalaryPeriod` (plus a legacy free-text `Salary`). +- `Tags` is a **JSON array in a string column** — not queryable; `/tags` and `/tag-trends` must scan. +- Denormalized `HasResume`/`HasCoverLetter`/`HasPortfolio`/`HasOtherAttachment` flags duplicate `Attachments`; kept honest by `AttachmentFlagsRecomputeTests`. +- CV text is stored **three times** (`CvExtractionRun.RawExtractedText`, `.NormalizedText`, `.StructuredProfileJson`) plus twice on the user. Deliberate audit trail, but **no retention policy**. +- `Status` is free-text at the DB level; canonicalized only in the application layer by `JobPipeline.Normalize` — deliberately, so custom user values are never destroyed. +- Indexes: `OwnerUserId` on Company/Job/JobApplication/GmailConnection; composites `(OwnerUserId, UploadedAtUtc)`, `(OwnerUserId, StartedAtUtc)`; unique `(OwnerUserId, JobApplicationId)` on draft, unique `(OwnerUserId, GmailAddress)`. EF auto-indexes FKs by convention. **Genuinely missing:** owner-prefixed composites `(OwnerUserId, IsDeleted, Status)` and `(OwnerUserId, FollowUpAt)`. +- SQLite at `DataRoot/jobtracker.db` (WAL); migrations applied at startup by `StartupInitializationExtensions` (1356 lines — also seeds admin, creates Identity tables where `dotnet ef` is unavailable, ignores `PendingModelChangesWarning`). + +> Corrected 2026-07-17: prior session notes recorded the EF model snapshot as **broken/empty**. It was **resynced** in `20260711181039_SyncModelSnapshot` — the snapshot now covers the full model and incremental `dotnet ef migrations add` works normally. Note `dotnet ef` still needs the `Design` package temporarily added to `JobTrackerApi` (the `MigrationsAssembly`), since it lives in `JobTrackerBackend` with `PrivateAssets=all`. + +--- + +## 7. API surface (19 controllers, all under `/api`) + +| Controller | Lines | Highlights | +|---|---|---| +| `JobApplicationsController` | **2313** | **38 endpoints.** CRUD, paging/filter/sort, board, reminders, stats, analytics, history, timeline, status/follow-up PATCH, soft delete/restore, duplicate-check, **plus** the whole AI surface: match-score, candidate-fit, focus-plan, interview-prep, readiness, tailored-CV draft/preview/export/generate, application-drafts, application-package, follow-up drafts + send, ai-metrics. | +| `ProfileCvController` | **2249** | CV upload artifacts, extraction runs, structure parsing, reprocess/rebuild/improve, rewrite-section, rewrite-preview, templates, Playwright PDF export, benchmark harness. | +| `GmailController` | **1023** | OAuth connect/callback, sync, review queue, import decisions, job matching. | +| `AuthController` | **879** | login/register/me/config, Google + Microsoft exchange and link/unlink, avatar, password change/reset, email verification, session cookie + CSRF. | +| `AdminSystemController` | 342 | System readiness (DB/Gmail/AI). | +| `TwoFactorController` | 341 | TOTP enrol/verify/disable, recovery codes. | +| `AttachmentsController` | 245 | Multipart upload to disk, download, rename, delete, purpose/AI-inclusion metadata. | +| `UsersController` | 229 | Admin user/role management. | +| `AdminAuditController` | 219 | Audit trail. | +| `CorrespondenceController` | 185 | Per-job messages CRUD. | +| `CompaniesController` | 150 | CRUD, idempotent create-by-name, recruiter/pipeline fields. | +| `MicrosoftGraphController` | 150 | Outlook/M365 mail linking. | +| `SessionsController` | 104 | List/revoke sessions. | +| `ExportController` | 102 | JSON/CSV export. | +| `RulesController` | 101 | Global + per-user rule settings, clamped. | +| `ClientErrorsController` | 100 | Frontend error intake → logs. | +| `ImapController` | 96 | IMAP mail linking (SSRF-guarded). | +| `BackupController` | 89 | Manual backup trigger. | +| `JobImportController` | **27** | **One endpoint:** `POST /preview`. URL parse only — no persistence, no import history. | + +**God controllers are a top debt.** `JobApplicationsController` and `ProfileCvController` mix HTTP, business logic, AI prompt construction, and persistence. `docs/MASTER_IMPLEMENTATION_GUIDE.md` forbids exactly this ("Avoid: Massive controllers"). Refactor needs test cover first — the tests exist. + +**OpenAPI** is wired (`AddOpenApi` / `MapOpenApi`) but **dev-only** — guarded by `app.Environment.IsDevelopment()`, not exposed in production. + +--- + +## 8. Background services (7 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 a configured local hour | +| `JobEnrichmentHostedService` | Backfills summaries/enrichment | +| `SummarizerProbeHostedService` | Probes AI service readiness | +| `CvProcessingHostedService` + `CvProcessingQueue` | In-memory queue for CV extraction | +| `DatabaseBackupHostedService` → `DatabaseBackupRunner` | Automated DB backup (`VACUUM INTO`, server-derived path) | + +**All state is in-process** (`IMemoryCache`, in-memory queue) — single-instance assumption, no distributed locks, **queued CV jobs are lost on restart**. + +--- + +## 9. AI pipeline + +**Architecture:** the backend does **not** call any LLM in-process. It HTTP-calls a FastAPI sidecar (`tools/summarizer/app.py`) exposing `/health`, `/cv/normalize`, `/cv/classify-block`, `/cv/rewrite`, `/summarize`, `/extract-text`. The sidecar picks **one** provider from the process-wide `AI_PROVIDER` env var ∈ {`ollama`, `gemini`, `groq`}. + +> **Important — `docs/00-ai-context.md` is wrong about this.** It describes a provider interface fanning out to OpenAI/Gemini/Claude/Ollama, admin-controlled, with users never locked to one model. **None of that exists.** There is one env var, one provider per deployment, no OpenAI, no Claude, no admin control, no per-user selection. Product decision 2026-07-17: **the docs get fixed, the abstraction does not get built** — revisit only if a customer asks. + +**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 → persisted. +2. **Summaries:** `SummarizerService` → `/summarize` (distilbart, TTL-cached, GPU if available) → persisted `ShortSummary`. +3. **CV ingest:** upload (PDF/DOCX/image) → `/extract-text` (OCR) → block classification (`CvAiClassifier`/`CvAiNormalizer` via `/cv/classify-block`) → `StructuredCvProfile` → `ProfileCvStructureJson` on the user. +4. **Tailoring:** job description + structured CV → `/cv/rewrite` → `TailoredCvDraft` (separate entity, per application) → `CvTemplateRenderer` → Playwright → PDF. +5. **Drafts:** cover letter / recruiter message / follow-up per job, attachment-aware context selection. + +**Invariant that holds:** the master profile is **never** auto-overwritten. Tailored output lands in `TailoredCvDraft`, a separate entity. This is the most important documented rule and it is correctly implemented — do not break it. + +**Degradation:** if the AI service or provider is down, core tracking still works (probe service; AI is not a deploy gate). + +**CV templates are hardcoded** — `CvTemplateRenderer.Render` is a C# `switch` over 6 template IDs (`ats-minimal`, `harvard`, `auckland`, `edinburgh`, `monarch`, `fjord`), each a function interpolating HTML strings, with booleans like `roundedPhoto`/`curvedHeader`. **There is no theme model and nothing is user-customisable.** This is a structural dead end for the CV Builder — see `docs/application-discovery-report.md` §10. + +--- + +## 10. Email + +`SmtpEmailSender` + `EmailSettingsResolver`: config from env/appsettings **or** DB-stored `SystemEmailSettings` (admin-editable). Gmail SMTP + app password in prod. Flows: password reset, email verification, follow-up reminders. `App:PublicBaseUrl` builds links. + +Inbound: `GmailOAuthService` (655), `MicrosoftGraphOAuthService` (507), `ImapService` (345, SSRF-guarded). + +--- + +## 11. Configuration & secrets + +- `.env` (git-ignored) → docker-compose env → ASP.NET config. `.env.example` documents the shape. +- `appsettings.Development.json` holds only `CHANGE_ME_*` placeholders. +- Key knobs: `Database:Provider`, `ConnectionStrings:JobTracker`, `Data:Root`, `Cors:Origins`, `Ai:BaseUrl`, `Summarizer:BaseUrl`, **`Ai:ServiceToken`**, `Auth:*` (incl. `Auth:AllowRegistration`), `Email:*`, `Exports:*`, `App:*`, `HttpsRedirection:*` (TLS terminated at the reverse proxy). +- AI service knobs (compose): `AI_PROVIDER`, **`AI_SERVICE_TOKEN`**, `OLLAMA_BASE_URL`, `OLLAMA_MODEL`, `GEMINI_API_KEY`, `GROQ_API_KEY`. +- **`AI_SERVICE_TOKEN` is mandatory.** Both `Ai__ServiceToken` (backend) and `AI_SERVICE_TOKEN` (ai-service) use `${AI_SERVICE_TOKEN:?...}`, so `docker compose up` fails loudly rather than booting an unauthenticated AI service. Generate with `python -c "import secrets; print(secrets.token_hex(32))"`. Rotating it requires recreating **both** containers together — they must agree. +- Note both those compose entries are **quoted**: the `:?` error message contains a colon-space, which YAML would otherwise parse as a map (`services.backend.environment.[20]: unexpected type map[string]interface{}`). +- `ProductionConfigTests.cs` guards prod config shape. +- Ollama is **intentionally not bundled by default** (`bundled-ollama` compose profile) so deploys reuse a shared instance. `AI_PROVIDER=gemini` exists specifically to offload a weak local GPU in prod. + +--- + +## 12. Build, CI/CD, deployment + +- **CI is Gitea, not GitHub** — `.gitea/workflows/ci-deploy.yml`. **There is no `.github/` directory.** +- On PR + push-to-main: build backend (Release) → run **all** backend tests → `npm ci` → run the **whole** frontend suite → build frontend. + + > Corrected 2026-07-17: the archived overview said CI runs "an explicit whitelist of 10 frontend test files". **The whitelist is gone.** The workflow now runs `npm test -- --watchAll=false --runInBand` and carries a comment forbidding its return: the previous whitelist "silently skipped new suites and let two regressions reach main." + +- The workflow is heavily defended against a flaky self-hosted runner: dotnet install retry, `npm ci` SIGSEGV retry, frontend build OOM retry. +- **Deploy** (push to main only): SSH to prod → `git reset --hard ` in `/opt/job-tracker/app` → `deploy/deploy.sh` (compose build/up with retry + cache-prune fallbacks) → verify containers. AI health is non-blocking. +- **No staging environment.** Deploys go straight to prod after CI. + +--- + +## 13. Testing + +- **Backend:** xUnit integration-style via `TestHostFactory`. 36 test files. Notable: `JobApplicationsAuthorizationTests`, `OwnershipGuardTests`, `ImapServiceSsrfGuardTests`, `ProductionConfigTests`, `AttachmentFlagsRecomputeTests`, `CvCorpusHarnessTests`, `SqliteMigrationHelperTests`, `JobPipelineTests`, plus a `tools/hostile-fixture-db` project. +- **Frontend:** ~20 Jest/RTL files — **all run in CI**. +- **AI service:** pytest (`tools/summarizer/tests/`). +- **Gaps:** no true end-to-end browser tests; no load/perf tests; **no dependency CVE scanning** (CI explicitly sets `npm_config_audit: 'false'`). + +--- + +## 14. Logging & error handling + +Console/debug logging; middleware logs every request (method, path, status, ms, traceId, sub claim). Unhandled exceptions logged then rethrown (500). Client errors POST to `/api/client-errors`. React `ErrorBoundary` + route error page. + +No structured sink (Seq/OTLP), no in-app log rotation, no ProblemDetails standardization. + +--- + +## 15. Security posture + +**Verified strong:** +- Multi-tenancy via deny-on-null global query filters, with a dedicated authorization test suite. +- CSRF double-submit on mutating requests; HttpOnly SameSite=Lax session cookie. +- Auth fails closed when required but unconfigured; subjectless-JWT rejected (M013-2). +- SSRF on job import and IMAP fixed and retested (DNS resolution check, private/loopback/link-local rejection, redirects disabled). +- Rate-limited login/email/2FA endpoints; Identity PBKDF2 hashing. +- OpenAPI dev-only. `.env` git-ignored; DP keys and runtime exports untracked (`519c32e`). +- 2FA + recovery codes + trusted devices + session revocation. +- **AI sidecar: backend-only.** Unpublished, on a private two-member network, and token-authenticated (§16). Verified against the running stack, not just configured. + +**Open findings** (detail in `docs/application-discovery-report.md` §12 and `docs/phase-0-foundation-report.md`): + +| Sev | Finding | Status | +|---|---|---| +| Medium | DataProtection keys recoverable from git history (`519c32e`, `955cae6`) | **Open — rotation required, needs an operator** | +| Medium | **CORS: `Cors:Origins="*"` triggers `SetIsOriginAllowed(_ => true)` + `AllowCredentials()`** (`Program.cs:96-102`) — reflected-origin with cookies = session theft from any site. Not currently active (compose never sets `Cors__Origins`, so it defaults to `localhost:3000`), but it is one config value away. | **Open — landmine** | +| Medium | No AI cost ceiling (no quota, no metering, unthrottled) | Open | +| Low | No CAPTCHA (rate limiting only) | Open — blocks public signup | +| Low | Unbounded storage: attachments, CV artifacts, extraction runs, base64 avatars | Open | +| Low | Backup / DPAPI is Windows-oriented — verify behaviour on Linux prod | Unverified | +| Low | No dependency CVE scanning in CI | Open | + +--- + +## 16. Phase 0 changes (2026-07-17) + +Full record: `docs/phase-0-foundation-report.md`. What changed architecturally: + +- **AI sidecar secured — three layers, verified against the running stack (2026-07-17):** + 1. **No host port.** `ports: "8001:8001"` removed; `expose:` only. + 2. **Private network.** `ai-service` sits on a new `ai_internal` bridge and **nothing else**. It was removed from `default` (which the frontend shares) and from `shared_services` — the latter is `external: true` (`jobtracker_shared`), so any other compose stack on the host could join it and reach port 8001. `ai_internal` has exactly two members: `ai-service` and `backend`. It is **not** `internal: true`, because ai-service needs egress to Gemini/Groq. + 3. **Shared secret.** `X-Ai-Service-Token` required on every endpoint except `/health`, compared with `hmac.compare_digest`. Backend sends it via `Ai:ServiceToken`; sidecar reads `AI_SERVICE_TOKEN`. Unset = open (local dev/tests), but compose declares both with `:?` so the stack **refuses to start** without it. + + **Only the backend can reach the AI service.** Verified live: host → connection refused; frontend container → cannot even resolve `ai-service`; unauthenticated calls to `/summarize`, `/cv/rewrite`, `/extract-text` → 401; wrong token → 401; backend (172.23.0.3) with token → **200 OK**. + + > If you point `OLLAMA_BASE_URL` at an Ollama in **another** compose stack, address it by host IP (e.g. `http://:11435`) — `ai-service` can no longer resolve container names on `shared_services`, by design. The bundled `ollama` profile is on `ai_internal` and still works by name. +- **Pipeline expanded beyond `Applied`** — `JobPipeline` now models pre-application stages (`Saved`, `Interested`, `Preparing`) in a new `PipelineCategory.Prospect`, so a job can be tracked before it is applied to. `Saved` is the new default for wizard-created jobs; `Applied` remains the default for the legacy create path. +- **`DateApplied` is nullable** + `SavedAt` added — a saved job no longer carries a fabricated application date. +- **`Job` entity introduced** alongside `JobApplication` (additive; `JobApplication.JobId` nullable FK). No behaviour moved yet — this only makes the split possible. + +--- + +## 17. Known debt (ranked) + +1. **`JobApplication` still carries opportunity columns** — the `Job` split is started but not completed. Reads/writes still use the legacy columns. +2. **God controllers** — `JobApplicationsController` 2313/38 endpoints, `ProfileCvController` 2249, `StartupInitializationExtensions` 1356, `GmailController` 1023, `AuthController` 879. +3. **Hardcoded CV templates** — dead end for the CV Builder. +4. **Three frontend toolchains** — Next shell + react-router (×2 patterns) + react-scripts test runner. +5. **No frontend data layer** — root cause of the 600–1400-line components. +6. **`ProfilePage` (1368 lines) serves two routes** behind a boolean; the `careerView` prop it accepts is **never read** (the "CV Builder" tab is inert). +7. **Denormalized `Has*` flags**; **`Tags` as a JSON string column**; **unbounded CV storage**; **base64 avatars in a DB column**. +8. **`JobTrackerBackend` link-compilation** — self-described "transitional". +9. **In-memory queue/cache** — single-instance coupling; restart loses queued CV jobs. +10. **No OpenAPI in prod, no ProblemDetails, no structured logging sink.** +11. **Root-level clutter**: `temp_job.json`, `temp_post_job.py`, `todo jobtracker.txt`, `test/`, `tmp/`, `vendor/`, `docs.7z`, `CV_Changes.md`, `SMART_GMAIL_PROGRESS.md`. +12. **Norway-only import plugins** (Finn/NAV/Jobbnorge). Product decision 2026-07-17: **Norway first, but no hardcoding Norway** — market must become a data dimension, not an assumption. + +--- + +## 18. Historical decisions worth knowing + +Recorded nowhere else in active docs: + +1. `JobTrackerBackend` link-compilation exists so tests can reach controllers without the web host — deliberate, self-described "transitional". +2. `Status` is free-text and canonicalized in the application layer **specifically so custom user values are never destroyed** (`JobPipeline.cs` docstring). Deliberate; do not "fix" it with a DB enum. +3. The CI frontend-test whitelist was removed after it "silently skipped new suites and let two regressions reach main." +4. Ollama is intentionally not bundled by default so deploys reuse a shared instance. +5. `AI_PROVIDER=gemini` exists to offload a weak local GPU in prod. +6. `TailoredCvDraft` is a separate entity specifically to guarantee the master CV is never auto-modified. diff --git a/docs/architecture/cv-rendering.md b/docs/architecture/cv-rendering.md new file mode 100644 index 0000000..f74ae48 --- /dev/null +++ b/docs/architecture/cv-rendering.md @@ -0,0 +1,56 @@ +# CV Rendering Architecture + +## Current Direction + +Keep: + +HTML/CSS + +↓ + +Playwright + +↓ + +PDF + + +--- + +# Separation + +Data + +↓ + +Renderer + +↓ + +Theme + +↓ + +Output + +--- + +# Requirements + +Themes should be independent. + +Adding themes should not require code changes to CV logic. + +--- + +# Future Outputs + +Possible: + +PDF. + +DOCX. + +HTML. + +Public profile. \ No newline at end of file diff --git a/docs/architecture/database.md b/docs/architecture/database.md new file mode 100644 index 0000000..b1165fb --- /dev/null +++ b/docs/architecture/database.md @@ -0,0 +1,78 @@ +# Database Architecture + +## Principles + +The database represents business concepts. + +--- + +# Core Entities + +## User + +Account information. + +--- + +## Job + +A job opportunity. + +--- + +## Application + +User's application to a job. + +--- + +## Company + +Organisation. + +--- + +## Career Profile + +Professional identity. + +--- + +## CV + +Generated documents. + +--- + +# Relationships + +User + +↓ + +Jobs + +↓ + +Applications + + +User + +↓ + +Career Profile + +↓ + +CV Versions + +--- + +# Rules + +Avoid duplicated data. + +Prefer references. + +Maintain migrations carefully. \ No newline at end of file diff --git a/docs/architecture/deployment.md b/docs/architecture/deployment.md new file mode 100644 index 0000000..ddac380 --- /dev/null +++ b/docs/architecture/deployment.md @@ -0,0 +1,3 @@ +# deployment + +TODO: Complete documentation. diff --git a/docs/architecture/file-storage.md b/docs/architecture/file-storage.md new file mode 100644 index 0000000..60a829b --- /dev/null +++ b/docs/architecture/file-storage.md @@ -0,0 +1,36 @@ +# File Storage Architecture + +## Files + +Examples: + +- CV uploads. +- Generated PDFs. +- Cover letters. +- Portfolio files. + +--- + +# Requirements + +Files must have: + +- Owner. +- Type. +- Metadata. +- Access rules. + +--- + +# Security + +Never expose direct file paths. + +--- + +# Future SaaS + +Support: + +Storage quotas. +Multiple providers. \ No newline at end of file diff --git a/docs/architecture/frontend.md b/docs/architecture/frontend.md new file mode 100644 index 0000000..e623a95 --- /dev/null +++ b/docs/architecture/frontend.md @@ -0,0 +1,64 @@ +# Frontend Architecture + +## Goals + +The frontend should be: + +- Maintainable. +- Consistent. +- Easy to extend. + +--- + +# Structure + +Organise by feature. + +Example: + +features/ + +jobs/ + +career/ + +cv-builder/ + +settings/ + +auth/ + + +--- + +# Components + +Prefer: + +Reusable components. + +Small focused components. + +--- + +# Pages + +Pages should: + +- Compose components. +- Handle routing. +- Manage page-level state. + +--- + +# State + +Avoid unnecessary global state. + +Use: + +- Local state. +- Feature state. +- Server state. + +Where appropriate. \ No newline at end of file diff --git a/docs/architecture/future.md b/docs/architecture/future.md new file mode 100644 index 0000000..4ca695e --- /dev/null +++ b/docs/architecture/future.md @@ -0,0 +1,3 @@ +# future + +TODO: Complete documentation. diff --git a/docs/architecture/migrations.md b/docs/architecture/migrations.md new file mode 100644 index 0000000..6bed16a --- /dev/null +++ b/docs/architecture/migrations.md @@ -0,0 +1,35 @@ +# Database Migration Strategy + +## Principles + +Migrations should be: + +- Small. +- Reviewable. +- Reversible where possible. + +--- + +# Avoid + +Large destructive migrations. + +--- + +# Before Migration + +Check: + +- Existing data. +- Relationships. +- Production impact. + +--- + +# After Migration + +Verify: + +- Application startup. +- Existing workflows. +- Data integrity. \ No newline at end of file diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md new file mode 100644 index 0000000..9b24b57 --- /dev/null +++ b/docs/architecture/overview.md @@ -0,0 +1,93 @@ +# Application Architecture Overview + +## Purpose + +Define the target architecture direction for Jobjakt. + +This document does not require an immediate rewrite. + +The goal is incremental improvement while keeping the application functional. + +--- + +# Product Architecture + +The application consists of: + +## Core Platform + +Job Tracking + +↓ + +Applications + +↓ + +Communication + +↓ + +Follow-ups + + +--- + +## Supporting Platform + +Career Workspace + +↓ + +Master Career Profile + +↓ + +CV Builder + +↓ + +Documents + + +--- + +## Intelligence Layer + +AI Services + +↓ + +Recommendations + +↓ + +Generation + +--- + +# Architecture Goals + +The system should be: + +- Maintainable. +- Testable. +- Extensible. +- Secure. +- SaaS-ready. + +--- + +# Evolution Strategy + +Prefer: + +- Refactoring. +- Extraction. +- Clear boundaries. +- Incremental migrations. + +Avoid: + +- Large rewrites. +- Replacing working systems unnecessarily. \ No newline at end of file diff --git a/docs/architecture/principles.md b/docs/architecture/principles.md new file mode 100644 index 0000000..6c2ee54 --- /dev/null +++ b/docs/architecture/principles.md @@ -0,0 +1,46 @@ +# Architecture Principles + +## 1. Keep Business Logic Separate + +Business rules should not live inside: + +- Controllers. +- React components. +- Database models. + +--- + +# 2. Single Responsibility + +Classes and components should have one purpose. + +--- + +# 3. Prefer Composition + +Create reusable services. + +Avoid giant classes. + +--- + +# 4. Backwards Compatibility + +Existing functionality should continue working during migrations. + +--- + +# 5. Explicit Over Clever + +Readable code is preferred over complex abstractions. + +--- + +# 6. Test Important Logic + +Prioritise testing: + +- Business rules. +- AI workflows. +- Document generation. +- Authentication. \ No newline at end of file diff --git a/docs/architecture/technical-debt.md b/docs/architecture/technical-debt.md new file mode 100644 index 0000000..393443b --- /dev/null +++ b/docs/architecture/technical-debt.md @@ -0,0 +1,56 @@ +# Technical Debt Management + +## Purpose + +Track known issues. + +--- + +# Categories + +## Architecture + +Examples: + +- Large services. +- Tight coupling. + +--- + +## Frontend + +Examples: + +- Duplicate components. +- Complex state. + +--- + +## Backend + +Examples: + +- Large controllers. +- Missing abstractions. + +--- + +## Testing + +Examples: + +- Missing coverage. + +--- + +# Rules + +Do not fix everything immediately. + +Prioritise: + +Impact. + +Risk. + +User value. \ No newline at end of file diff --git a/docs/auth/2fa.md b/docs/auth/2fa.md new file mode 100644 index 0000000..48e1d76 --- /dev/null +++ b/docs/auth/2fa.md @@ -0,0 +1,3 @@ +# 2fa + +TODO: Complete documentation. diff --git a/docs/auth/login.md b/docs/auth/login.md new file mode 100644 index 0000000..326fcef --- /dev/null +++ b/docs/auth/login.md @@ -0,0 +1,3 @@ +# login + +TODO: Complete documentation. diff --git a/docs/auth/oauth.md b/docs/auth/oauth.md new file mode 100644 index 0000000..5831135 --- /dev/null +++ b/docs/auth/oauth.md @@ -0,0 +1,3 @@ +# oauth + +TODO: Complete documentation. diff --git a/docs/auth/overview.md b/docs/auth/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/auth/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/auth/signup.md b/docs/auth/signup.md new file mode 100644 index 0000000..64148d3 --- /dev/null +++ b/docs/auth/signup.md @@ -0,0 +1,3 @@ +# signup + +TODO: Complete documentation. diff --git a/docs/career-workspace-branch-assessment.md b/docs/career-workspace-branch-assessment.md new file mode 100644 index 0000000..7a6c1b8 --- /dev/null +++ b/docs/career-workspace-branch-assessment.md @@ -0,0 +1,158 @@ +# `feature/career-workspace` — Branch Assessment + +Date: 2026-07-17 +Trigger: your Phase 3 decision on the "orphan tables". The investigation you asked for changed the answer entirely. +Status: **decision required before Phase 3 or Phase 4 can be planned honestly.** + +--- + +## 1. The finding + +You asked me to treat the orphan tables as abandoned prototypes and redesign around them. **They are not prototypes.** They are the local footprint of a **complete, tested, unmerged feature branch**. + +`feature/career-workspace` exists **locally and on `origin`**, 10 commits, last touched **2026-07-12** — five days ago. It was never reverted. It was simply never merged. + +The tables appeared in the dev database because that branch was checked out and run: its schema reconciler created them. The code was then switched away, leaving the tables behind. + +**My discovery report got this wrong.** I concluded "Novoresume, Reactive Resume and ElegantCV are not analysed anywhere in this repo" and listed CV variants as "planned, zero code". Both are false. The audit searched the working tree, `main`, and `docs/_archive/` — **it never ran `git branch -a`.** Corrections are recorded in `docs/application-discovery-report.md` §8 and its appendix. + +--- + +## 2. What is on the branch + +### Research and strategy (~1,100 lines, none of it on `main`) + +| Document | Lines | Content | +|---|---|---| +| `cv-builder-competitor-deep-research.md` | 327 | Teardowns of **Novoresume, Reactive Resume, FlowCV, Teal, Enhancv, Canva, Resume.io, Kickresume**; feature matrix; business-model analysis | +| `career-workspace-product-strategy.md` | 328 | Vision, positioning, four personas (Marcus/Dana/Sofia/Tom) | +| `cv-builder-product-teardown.md` | 321 | Product teardown | +| `career-workspace-implementation-roadmap.md` | 124 | ADR-grade plan, phases F0–F5, migration mechanics, product boundary | + +**This is the exact research roadmap task 4.9 was scheduled to produce**, and it independently reaches the same conclusions my audit did from the FlowCV files: + +- *"The winning editor model is structured-form + live preview, not canvas editing and not rigid wizard flows."* +- Client-side live preview is *"a hard UX requirement, not a nice-to-have"*. +- Themes must be declarative data, a *"sibling input to renderer, never welded to content"*. + +Two independent analyses converging is strong corroboration for the Phase 4 direction. + +It also carries pricing intelligence Phase 7 needs and `main` does not have: Resume.io holds an **F BBB rating** over trial-to-subscription billing traps despite a 4.3 Trustpilot score; Novoresume blocks re-download of CVs the user **already paid for**; Kickresume caps "unlimited" AI mid-cycle. Its conclusion — *"never hold the user's data hostage"* — independently arrives at your "gate on capability, never count" decision. + +### Working code, with tests + +| Area | Detail | +|---|---| +| `CareerProfileService` (169 lines) | Dual-writes `CareerProfiles`/`CareerProfileVersions` from every existing save path. **Stable item IDs** assigned to jobs/education/certifications/projects — the stated prerequisite for variants referencing "this job" by identity rather than array position. `CvDateNormalizer` for free-text → `YYYY-MM`. | +| `CvVariant` / `CvVersion` / `TailoredApplication` | Dual-written from both `TailoredCvDraft` save paths. `CvVariant` carries a **`ThemeId`** — the theme seam Phase 4 needs. | +| `InterviewPrepNote` / `AiWorkspaceNote` | Persist AI output keyed by an `AttachmentContextSignature`, so interview prep and candidate fit stop regenerating on every open. | +| Frontend | CV rewrite diff view (`TextDiff`), ATS-safety badge per template, **`fix: unlock CV builder for Google/Microsoft-authenticated users`**. | +| Tests | **5 new backend suites** (`CareerProfileServiceTests`, `CvVariantSyncTests`, `InterviewPrepPersistenceTests`, `AiWorkspaceNotePersistenceTests`, `CvTemplateRendererTests`) + `text-diff.test.tsx`, and it **touches `profile-page.test.tsx`** — one of the two suites currently failing on `main`. | + +### Data + +**All 7 tables are empty (0 rows) in the dev database.** There is nothing to migrate and nothing to lose. Your "do not delete until a migration strategy exists" constraint is satisfied trivially — but it is also now moot, because the right move is almost certainly not deletion. + +``` +CareerProfiles rows=0 Id, OwnerUserId, ProfileJson, Version, CreatedAtUtc, UpdatedAtUtc +CareerProfileVersions rows=0 + CareerProfileId FK, Source (append-only history) +CvVariants rows=0 + CareerProfileId FK, Name, ContentJson, ThemeId, Version +CvVersions rows=0 + CvVariantId FK (append-only history) +TailoredApplications rows=0 CvVariantId FK + JobApplicationId FK (the join) +InterviewPrepNotes rows=0 + AttachmentContextSignature, TalkingPointsJson, LikelyQuestionsJson, WeakSpotsJson +AiWorkspaceNotes rows=0 + NoteType, AttachmentContextSignature, ResultJson +``` + +**Caveat: this is the local dev database. I cannot see production.** Confirm prod is also empty before acting. + +--- + +## 3. How it aligns with your decisions + +| Your decision (2026-07-17) | Branch position | Verdict | +|---|---|---| +| CareerProfile — **YES** | `CareerProfiles` + `CareerProfileService`, dual-written | ✅ Built | +| CareerProfileVersions — **YES, history/versioning** | Append-only, one row per save, with `Source` | ✅ Built, exactly as you described | +| CvVariants — **YES, redesign around generated variants from the master profile** | `CvVariant` hangs off `CareerProfileId`, survives job deletion, reusable across applications | ✅ Built, and already matches your redesign | +| CvVersions — **YES, generated document history** | Append-only per variant | ✅ Built | +| TailoredApplications — **YES, job-specific snapshots** | The join; *"a job application REFERENCES a tailored output; it does not own it"* | ✅ Built | +| InterviewPrepNotes — **YES, lower priority** | Built, with cache-invalidation signature | ✅ Built | +| Do not delete until a migration strategy exists | Tables empty; branch has both SQLite + MySQL reconciler dialects | ✅ Satisfied | + +**Every one of your six keep/rework verdicts is already implemented on that branch.** The one place it diverges from a later decision: the profile core is stored as `ProfileJson` (a blob), whereas you decided on **relational** for Experience/Education/Skills/Projects. The branch's *versioning* and *variant/tailoring* structure is unaffected by that; only the profile core's storage shape would need rework — and its **stable item IDs** work is a prerequisite for the relational model either way. + +It also independently states the same product boundary you have been enforcing: *"The primary product stays Job Search & Application Management. The Career Workspace is a bounded supporting domain."* + +--- + +## 4. Cost of each option + +Branch is **10 commits ahead, 29 commits behind** `main`. Seven files were touched on both sides since the fork: + +``` +Data/JobTrackerContext.cs +JobTrackerApi/Controllers/JobApplicationsController.cs +JobTrackerApi/Controllers/ProfileCvController.cs +JobTrackerApi/Program.cs +JobTrackerApi/Services/StartupInitializationExtensions.cs +job-tracker-ui/src/components/JobDetailsDialog.tsx +job-tracker-ui/src/views/ProfilePage.tsx +``` + +Conflicts are real but bounded, and **Phase 0 made them worse in two of those files** (`JobTrackerContext.cs`, `JobApplicationsController.cs`) — the cost grows every day main moves. + +| Option | Cost | Risk | +|---|---|---| +| **A. Rebase and merge the branch** | ~1–2 days of conflict resolution across 7 files | Recovers ~2,750 lines incl. 5 test suites and 1,100 lines of research. Conflicts grow with delay. | +| **B. Cherry-pick the docs now, code later** | ~1 hour | Unblocks 4.9 and Phase 7 pricing immediately at near-zero risk; leaves the code rotting further. | +| **C. Re-derive on main (the current roadmap plan)** | Weeks | Wastes the research and the tests; near-certain to produce a worse design than the one already reviewed. | +| **D. Abandon the branch, drop the tables** | ~1 hour | Throws away the strongest Phase 3/4 asset in the repo. Hard to justify given the tables are empty and the code is tested. | + +**Recommendation: B immediately, then A.** Cherry-pick the four documents onto `main` today — they are pure additions, cannot conflict, and immediately unblock roadmap 4.9 and the Phase 7 pricing model. Then schedule the rebase before Phase 3 starts, while the conflict surface is still seven files. + +**I have not touched the branch.** No rebase, no cherry-pick, no merge. + +--- + +## 5. What this changes in the roadmap + +- **1.9** — no longer "investigate orphan tables"; now "decide on the branch". Promoted to **P0**: it blocks honest planning of Phases 3 and 4. +- **4.9** — already done, on the branch. Recover, don't redo. +- **Phase 3** — F1/F2 equivalents already exist. The estimate (~2–3 wk) is wrong until the branch decision is made. +- **Phase 4** — the theme seam (`CvVariant.ThemeId`) and the research backing 4.1 both exist. +- **Phase 7** — the branch's pricing research independently supports the capability-not-count decision. +- **1.8** (red CI) — the branch touches `profile-page.test.tsx`, one of the two failing suites. Possibly related; check before fixing blind. + +--- + +## 6. Architecture review — Recover / Migrate / Replace + +Requested 2026-07-17. Verdict per table, judged against the current architecture and your 2026-07-17 decision that the profile core is **relational** for Experience/Education/Skills/Projects with a blob for the long tail. + +| Table | Verdict | Reasoning | +|---|---|---| +| **CareerProfiles** | **Recover the entity, replace the storage shape** | Lifting the profile off the Identity row (`ApplicationUser.ProfileCvStructureJson`) is right and is the whole point of the table — keep the entity, the per-user cardinality, `Version`, and the dual-write lifecycle. But `ProfileJson` is a blob, and you decided the core is relational. **Replace `ProfileJson` with relational Experience/Education/Skills/Projects + a blob column for the long tail** (awards, publications, organisations, references, custom sections). The entity survives; only its storage changes. | +| **CareerProfileVersions** | **Recover as-is** | Append-only, one row per save, with a `Source` discriminator (`upload`/`rebuild`/`improve`/`reprocess`/`parse`/`manual`). A **blob snapshot is the correct shape for history even in a relational world** — a version is an immutable point-in-time record, not something you query field-by-field. Do not relationalise this one. Directly delivers your "useful for history/versioning". | +| **CvVariants** | **Recover the entity, migrate the content model** | The entity already matches your redesign: it hangs off `CareerProfileId`, survives job deletion, is reusable across applications, and carries `ThemeId` (the Phase 4 theme seam). **But its own design doc says a variant is a *"persistent lens on the profile: selections + per-item overrides"*, while the shipped model stores `ContentJson` — a full serialized `TailoredCvDocument`, i.e. a *snapshot*.** The commit is explicit that this was a deliberate interim ("reuses the existing document shape… so this phase carries zero data-shape risk"). A snapshot **duplicates profile data**, which `MASTER_IMPLEMENTATION_GUIDE.md` forbids ("The CV builder is NOT the user's data… Never duplicate user data between templates"). **Migrate `ContentJson` → selections + per-item overrides referencing the stable item IDs that F1 already added for exactly this purpose.** | +| **CvVersions** | **Recover as-is** | Append-only history of a variant. Same reasoning as `CareerProfileVersions`: snapshots are correct for history. Delivers your "generated document history". | +| **TailoredApplications** | **Recover as-is** | The `CvVariant ↔ JobApplication` join. Encodes the product boundary you have been enforcing all along — *"a job application REFERENCES a tailored output; it does not own it"*. Delivers your "job-specific application snapshots". Note this is the same reference-not-ownership shape as Phase 0's `JobApplication.JobId`; the two are consistent. | +| **InterviewPrepNotes** | **Recover as-is** (low priority, as you said) | Caches AI interview prep keyed by an `AttachmentContextSignature`, so it stops regenerating on every open. `main` recomputes live. With `AI_PROVIDER=gemini` this is **real money per open**, so it is a cost fix, not just latency. Cache invalidation is already handled by the signature. | +| **AiWorkspaceNotes** | **Recover as-is** | Not in your list, but the same pattern generalised (`NoteType` + signature) and the sibling of the above — it backs the persisted candidate-fit and focus-plan commits. Recover with `InterviewPrepNotes` or neither; splitting them makes no sense. | + +**Nothing is Replace-and-discard. Nothing needs data migration — all 7 tables are empty in dev** (verify prod). The only genuine rework is two storage shapes: `CareerProfiles.ProfileJson` → relational core, and `CvVariants.ContentJson` → lens. + +### Sequencing consequence + +The two reworks are the *same shape of change* and share a prerequisite: **stable item IDs**, which F1 already implements. Recovering F1 first therefore unblocks both. Recommended order: recover F1 (profile + versions + stable IDs) → replace the profile core storage → recover F2 (variants + join) → migrate variants to a lens → recover the AI-cache tables. + +### One merge conflict worth naming + +The branch provisions tables through the **raw-SQL reconciler** (both SQLite and MySQL dialects), because when it was written "the EF ModelSnapshot is known-stale; do not rely on `Migrate()`". **That is no longer true** — Phase 0 resynced the snapshot and shipped `Jobs` via a normal EF migration, verified against the real dev database. On merge, both mechanisms will coexist. That is safe (the reconciler is idempotent) and should **not** be untangled during the rebase — do it as separate follow-up work, or the rebase becomes unreviewable. + +--- + +## 7. Open questions + +1. **Why was it never merged?** No revert exists, and it is pushed to `origin`. If it was parked for a reason I cannot see, that reason should be recorded before anyone picks it up. +2. **Is production also empty** on those 7 tables? I can only see the dev database. +3. **Does the branch's `a4c8e4a fix: unlock CV builder for Google/Microsoft-authenticated users` overlap your current uncommitted work** on `ProfilePage.tsx` / `CareerWorkspacePage.tsx` (the inert `careerView` tab)? Both touch the same file. Worth checking before either is finished. diff --git a/docs/career/career-profile.md b/docs/career/career-profile.md new file mode 100644 index 0000000..05e27d1 --- /dev/null +++ b/docs/career/career-profile.md @@ -0,0 +1,94 @@ + +# Career Profile + +## Purpose + +The Career Profile is the user's professional source of truth. + +--- + +# Information Stored + +## Personal Information + +- Name. +- Professional title. +- Contact information. +- Location. +- Profile photo. + +--- + +## Experience + +Contains: + +- Company. +- Role. +- Dates. +- Description. +- Achievements. +- Technologies. + +--- + +## Education + +Contains: + +- Institution. +- Qualification. +- Dates. +- Details. + +--- + +## Skills + +Contains: + +- Technical skills. +- Soft skills. +- Proficiency. + +--- + +## Projects + +Contains: + +- Name. +- Description. +- Technologies. +- Outcomes. + +--- + +## Certifications + +Contains: + +- Certification. +- Provider. +- Date. + +--- + +## Languages + +Contains: + +- Language. +- Level. + +--- + +# User Control + +Users must be able to: + +- Edit manually. +- Add information. +- Remove information. + +AI assists but never becomes the source of truth. \ No newline at end of file diff --git a/docs/career/cover-letters.md b/docs/career/cover-letters.md new file mode 100644 index 0000000..5277c9c --- /dev/null +++ b/docs/career/cover-letters.md @@ -0,0 +1,39 @@ +# Cover Letters + +## Purpose + +Generate professional application letters. + +--- + +# Inputs + +- Career Profile. +- Master CV. +- Job description. + +--- + +# Builder + +Support: + +- Templates. +- Formatting. +- AI assistance. + +--- + +# User Control + +Allow: + +- Editing. +- Free text. +- Uploading existing letters. + +--- + +# Future + +Cover letter themes matching CV themes. \ No newline at end of file diff --git a/docs/career/cv-ai.md b/docs/career/cv-ai.md new file mode 100644 index 0000000..f996e0d --- /dev/null +++ b/docs/career/cv-ai.md @@ -0,0 +1,46 @@ +# AI CV Features + +## Purpose + +Use AI to improve applications. + +--- + +# Supported Actions + +## Writing Assistance + +- Improve bullet points. +- Fix grammar. +- Make concise. +- Suggest achievements. + +--- + +## Generation + +Generate: + +- CV sections. +- Cover letters. +- Summaries. + +--- + +# Rules + +AI must: + +- Use existing user data. +- Avoid inventing experience. +- Require approval. + +--- + +# Future + +AI career assistant. + +Interview preparation. + +Career recommendations. \ No newline at end of file diff --git a/docs/career/cv-builder b/docs/career/cv-builder new file mode 100644 index 0000000..e69de29 diff --git a/docs/career/cv-rendering.md b/docs/career/cv-rendering.md new file mode 100644 index 0000000..fa79b06 --- /dev/null +++ b/docs/career/cv-rendering.md @@ -0,0 +1,68 @@ +# CV Rendering Architecture + +## Current Technology + +The application uses: + +HTML/CSS + +↓ + +Playwright + +↓ + +PDF + +--- + +# Recommended Architecture + +Keep this approach. + +It provides: + +- Excellent print output. +- CSS flexibility. +- Browser rendering. + +--- + +# Separation + +CV Data + +↓ + +Renderer + +↓ + +Theme + +↓ + +Output + +--- + +# Requirements + +Adding a new theme should not require: + +- Changing business logic. +- Changing database structure. +- Rewriting export code. + +--- + +# Supported Outputs + +Primary: + +- PDF. + +Future: + +- DOCX. +- HTML. \ No newline at end of file diff --git a/docs/career/cv-tailoring.md b/docs/career/cv-tailoring.md new file mode 100644 index 0000000..53b2e01 --- /dev/null +++ b/docs/career/cv-tailoring.md @@ -0,0 +1,53 @@ +# Tailored CV Generation + +## Purpose + +Create job-specific CVs. + +--- + +# Flow + +Job + +↓ + +Analyse requirements + +↓ + +Compare with Master CV + +↓ + +Suggest changes + +↓ + +Generate tailored version + +↓ + +User approves + +↓ + +Save against application + +--- + +# Important + +The Master CV remains unchanged. + +--- + +# Stored Data + +Save: + +- Job reference. +- Generated CV. +- Date. +- Theme settings. +- AI changes. \ No newline at end of file diff --git a/docs/career/cv-themes.md b/docs/career/cv-themes.md new file mode 100644 index 0000000..654f168 --- /dev/null +++ b/docs/career/cv-themes.md @@ -0,0 +1,78 @@ +# CV Theme System + +## Purpose + +Themes control presentation. + +They do not control data. + +--- + +# Initial Theme Library + +Create: + +3-5 professional themes. + +Examples: + +## Professional + +Traditional ATS-friendly. + +--- + +## Modern + +Clean two-column layout. + +--- + +## Creative + +More visual. + +--- + +## Technical + +Developer-focused. + +--- + +## Minimal + +Simple and elegant. + +--- + +# Theme Controls + +Users can customise: + +- Accent colour. +- Fonts. +- Photo visibility. +- Icons. +- Spacing. +- Layout. + +--- + +# Theme Requirements + +Themes must: + +- Work with all CV sections. +- Export correctly. +- Remain ATS-friendly. + +--- + +# Future + +Premium themes. + +Marketplace. + +Custom templates. \ No newline at end of file diff --git a/docs/career/documents.md b/docs/career/documents.md new file mode 100644 index 0000000..44d1723 --- /dev/null +++ b/docs/career/documents.md @@ -0,0 +1,34 @@ +# Career Documents + +## Purpose + +Manage application files. + +--- + +# Supported Files + +- CVs. +- Cover letters. +- Certificates. +- Portfolio files. +- Supporting documents. + +--- + +# Requirements + +Support: + +- Upload. +- Preview. +- Download. +- Attach to applications. + +--- + +# Storage + +User controlled limits. + +Future premium tiers may increase storage. \ No newline at end of file diff --git a/docs/career/education.md b/docs/career/education.md new file mode 100644 index 0000000..5756ec8 --- /dev/null +++ b/docs/career/education.md @@ -0,0 +1,3 @@ +# education + +TODO: Complete documentation. diff --git a/docs/career/experience.md b/docs/career/experience.md new file mode 100644 index 0000000..a348cc0 --- /dev/null +++ b/docs/career/experience.md @@ -0,0 +1,3 @@ +# experience + +TODO: Complete documentation. diff --git a/docs/career/future-features.md b/docs/career/future-features.md new file mode 100644 index 0000000..1c2ea8a --- /dev/null +++ b/docs/career/future-features.md @@ -0,0 +1,48 @@ +# Future Career Features + +Potential additions: + +## LinkedIn Integration + +Generate: + +- About section. +- Experience summaries. + +--- + +## Interview Preparation + +Generate: + +- Questions. +- Answers. +- Preparation plans. + +--- + +## Skill Analysis + +Compare: + +Career profile + +against + +Job requirements. + +--- + +## Career Planning + +Support: + +- Learning paths. +- Skill gaps. +- Goals. + +--- + +# Priority + +Only add features that improve career outcomes. \ No newline at end of file diff --git a/docs/career/master-profile.md b/docs/career/master-profile.md new file mode 100644 index 0000000..42002d0 --- /dev/null +++ b/docs/career/master-profile.md @@ -0,0 +1,52 @@ +# Master CV + +## Purpose + +The Master CV represents the user's complete professional history. + +It is not necessarily the CV sent to employers. + +--- + +# Relationship + +Career Profile + +↓ + +Master CV + +↓ + +Tailored CV + +--- + +# Behaviour + +The Master CV contains: + +- All experience. +- All skills. +- All projects. +- All achievements. + +A user may have more information here than appears in a final CV. + +--- + +# Example + +Master CV: + +10 years experience. + +Generated CV: + +Only relevant 5 years shown. + +--- + +# Rules + +Never overwrite master CV when creating tailored CVs. \ No newline at end of file diff --git a/docs/career/overview.md b/docs/career/overview.md new file mode 100644 index 0000000..f6ae759 --- /dev/null +++ b/docs/career/overview.md @@ -0,0 +1,87 @@ +# Career Workspace Overview + +## Purpose + +The Career Workspace is a supporting feature of Jobjakt. + +It helps users maintain their professional identity and create career outputs. + +The primary product remains job tracking. + +The Career Workspace exists to make applications easier and more effective. + +--- + +# Core Concept + +Users maintain one professional source of truth: + +Career Profile + +↓ + +Master CV + +↓ + +CV Builder + +↓ + +Tailored Applications + +↓ + +Career Outputs + +--- + +# Outputs + +The Career Workspace can generate: + +- Standard CV. +- Job-specific CV. +- Cover letters. +- Portfolio. +- Public profile. +- LinkedIn summaries. +- Interview preparation. + +--- + +# Important Rule + +Do not create separate disconnected CV profiles. + +The user has: + +ONE career identity. + +Many possible outputs. + +--- + +# Architecture Principle + +Career data should not contain presentation information. + +Example: + +Career Profile: + +"Worked as Software Engineer at Company X" + +Theme: + +"Modern Professional" + +Output: + +"PDF CV" + +--- + +# Product Goal + +The user should enter information once and reuse it everywhere. \ No newline at end of file diff --git a/docs/career/portfolio.md b/docs/career/portfolio.md new file mode 100644 index 0000000..2de76a5 --- /dev/null +++ b/docs/career/portfolio.md @@ -0,0 +1,25 @@ +# Portfolio + +## Purpose + +Allow users to showcase work. + +--- + +# Sections + +Possible: + +- Projects. +- Case studies. +- Links. +- Images. +- Documents. + +--- + +# Relationship + +Portfolio is part of Career Workspace. + +Not required for every user. \ No newline at end of file diff --git a/docs/career/projects.md b/docs/career/projects.md new file mode 100644 index 0000000..da3d08a --- /dev/null +++ b/docs/career/projects.md @@ -0,0 +1,3 @@ +# projects + +TODO: Complete documentation. diff --git a/docs/career/public-profile.md b/docs/career/public-profile.md new file mode 100644 index 0000000..4a9e9b4 --- /dev/null +++ b/docs/career/public-profile.md @@ -0,0 +1,46 @@ +# Public Career Profile + +## Purpose + +Allow users to share their professional profile. + +--- + +# URL + +Example: + +/cv/{random-guid} + +--- + +# Features + +Display: + +- CV. +- Experience. +- Skills. +- Portfolio. + +--- + +# Privacy + +User controls: + +Public. + +Private. + +Disabled. + +--- + +# Security + +Use: + +- Random identifiers. +- Rate limiting. +- Abuse protection. \ No newline at end of file diff --git a/docs/career/skills.md b/docs/career/skills.md new file mode 100644 index 0000000..83b2242 --- /dev/null +++ b/docs/career/skills.md @@ -0,0 +1,3 @@ +# skills + +TODO: Complete documentation. diff --git a/docs/career/versioning.md b/docs/career/versioning.md new file mode 100644 index 0000000..f0d40a9 --- /dev/null +++ b/docs/career/versioning.md @@ -0,0 +1,35 @@ +# Career Versioning + +## Purpose + +Allow users to recover previous work. + +--- + +# Version Types + +## Master CV Versions + +Snapshots of career information. + +--- + +## Generated CV Versions + +Copies created for applications. + +--- + +# Features + +Allow: + +- Save versions. +- Compare. +- Restore. + +--- + +# Important + +History should protect users from accidental changes. \ No newline at end of file diff --git a/docs/cover-letters/ai.md b/docs/cover-letters/ai.md new file mode 100644 index 0000000..0c0a278 --- /dev/null +++ b/docs/cover-letters/ai.md @@ -0,0 +1,3 @@ +# ai + +TODO: Complete documentation. diff --git a/docs/cover-letters/builder.md b/docs/cover-letters/builder.md new file mode 100644 index 0000000..bd7421a --- /dev/null +++ b/docs/cover-letters/builder.md @@ -0,0 +1,3 @@ +# builder + +TODO: Complete documentation. diff --git a/docs/cover-letters/overview.md b/docs/cover-letters/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/cover-letters/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/cv-builder/ai.md b/docs/cv-builder/ai.md new file mode 100644 index 0000000..0c0a278 --- /dev/null +++ b/docs/cv-builder/ai.md @@ -0,0 +1,3 @@ +# ai + +TODO: Complete documentation. diff --git a/docs/cv-builder/content.md b/docs/cv-builder/content.md new file mode 100644 index 0000000..533130a --- /dev/null +++ b/docs/cv-builder/content.md @@ -0,0 +1,3 @@ +# content + +TODO: Complete documentation. diff --git a/docs/cv-builder/customisation.md b/docs/cv-builder/customisation.md new file mode 100644 index 0000000..73b24e8 --- /dev/null +++ b/docs/cv-builder/customisation.md @@ -0,0 +1,3 @@ +# customisation + +TODO: Complete documentation. diff --git a/docs/cv-builder/exports.md b/docs/cv-builder/exports.md new file mode 100644 index 0000000..247610d --- /dev/null +++ b/docs/cv-builder/exports.md @@ -0,0 +1,3 @@ +# exports + +TODO: Complete documentation. diff --git a/docs/cv-builder/overview.md b/docs/cv-builder/overview.md new file mode 100644 index 0000000..a9d997c --- /dev/null +++ b/docs/cv-builder/overview.md @@ -0,0 +1,3 @@ +# overview + +TODO: Complete documentation. diff --git a/docs/cv-builder/rendering.md b/docs/cv-builder/rendering.md new file mode 100644 index 0000000..007182c --- /dev/null +++ b/docs/cv-builder/rendering.md @@ -0,0 +1,3 @@ +# rendering + +TODO: Complete documentation. diff --git a/docs/cv-builder/themes.md b/docs/cv-builder/themes.md new file mode 100644 index 0000000..6c609bb --- /dev/null +++ b/docs/cv-builder/themes.md @@ -0,0 +1,3 @@ +# themes + +TODO: Complete documentation. diff --git a/docs/cv-builder/workflow.md b/docs/cv-builder/workflow.md new file mode 100644 index 0000000..8c16632 --- /dev/null +++ b/docs/cv-builder/workflow.md @@ -0,0 +1,3 @@ +# workflow + +TODO: Complete documentation. diff --git a/docs/decisions/ADR-002-job-application-model.md b/docs/decisions/ADR-002-job-application-model.md new file mode 100644 index 0000000..7556e49 --- /dev/null +++ b/docs/decisions/ADR-002-job-application-model.md @@ -0,0 +1,109 @@ +# ADR-002 — Separating Job (the opportunity) from JobApplication (the pursuit) + +- **Status:** Accepted (partially implemented) +- **Date:** 2026-07-17 +- **Phase:** 0 (foundation corrections) +- **Supersedes:** the 0-byte placeholder previously at `docs/_archive/decisions/ADR-002-job-application-model.md` + +--- + +## Context + +`docs/MASTER_IMPLEMENTATION_GUIDE.md` names this as the product's #2 priority workflow: + +``` +Find Job → Import Job → Review Details → Prepare CV → Prepare Cover Letter + → Add Supporting Files → Submit Application → Track Progress +``` + +`docs/01-glossary.md` is explicit that "A Job may exist before an application is submitted" and models the hierarchy as `Company → Job Opportunity → Application`. + +**The code could not represent any of that.** The 2026-07-17 discovery audit found: + +1. There was no `Job` entity. `JobApplication` was the only entity, carrying 43 members that mixed opportunity data (`JobTitle`, `Description`, `JobUrl`, `Salary*`, `Deadline`, `Tags`, `Location`) with application data (`Status`, `DateApplied`, `ResponseReceived`, `FollowUpAt`). +2. `JobPipeline.Stages` began at `Applied`. There was no `Saved`/`Interested`/`Preparing`. +3. `DateApplied` was non-nullable with a `DateTime.UtcNow` default. + +Consequences: + +- A job you had not applied to could not be tracked. The 6-step add-job wizard (`components/AddJobModal.tsx`) walks a user through *preparing* an application and then had nowhere to save it except `Applied` with a fabricated date. +- Applying twice to the same reposted role duplicated the entire job description. +- Teal — the market leader — offers `Saved` in its **free** tier. This was a table-stakes gap. + +## Decision + +Split `Job` from `JobApplication`, **additively and in stages**. Phase 0 lays the foundation only. + +### Delivered in Phase 0 + +1. **`Job` entity** (`Models/Job.cs`) — the opportunity: company, title, description, URL, location, salary, deadline, tags, plus `SavedAt`. Owner-scoped by the same deny-on-null global query filter as every other tenant entity. +2. **`JobApplication.JobId`** — a nullable FK to `Job`, `OnDelete: SetNull`. +3. **Prospect stages** — `JobPipeline` gained `PipelineCategory.Prospect` and the stages `Saved`(1), `Interested`(2), `Preparing`(3), ahead of `Applied`(4). No schema change: `Status` is a free-text column by deliberate prior design. +4. **`DateApplied` is now nullable**, and `SavedAt` was added to `JobApplication`. +5. **`JobPipeline.SyncAppliedDate`** — the single enforcement point for the invariant below. + +### Explicitly NOT done in Phase 0 + +- **No dual-write.** Nothing writes or reads `Job` yet. `JobApplication` remains the sole source of truth for every read and write. +- **No backfill of `Job` rows.** The table ships empty. +- **No legacy columns dropped.** `JobApplication` keeps its full copy of the opportunity fields. + +This keeps Phase 0 a pure schema-and-vocabulary change with **zero behavioural change** to existing workflows, which is what "unblock future phases safely" requires. + +### The invariant + +> `DateApplied` is set **if and only if** the job has left the pre-application stages. + +Enforced in exactly one place — `JobPipeline.SyncAppliedDate(job, now)` — called from all three status-write paths (`POST /jobapplications`, `PUT /jobapplications/{id}`, `PATCH /jobapplications/{id}/status`) so they cannot drift. + +- Entering a real stage stamps `DateApplied` if unset. +- Moving **back** into a Prospect stage **clears** it. + +The backward clear is deliberate. The alternative — a `Saved` job still carrying an applied date — would silently count it as applied in analytics and expose it to the follow-up/ghosting rules. The original date stays recoverable from the `StatusChanged` `JobEvent` history, so this is denormalised-field loss, not data loss. + +## Consequences + +### Safe by construction + +- **`RulesEngine`** already whitelisted `Applied`/`Offer`/`Rejected`/`Waiting` and explicitly refused to ghost anything else, so prospects were never at risk. A `JobPipeline.IsProspect` guard was added anyway, ahead of any date arithmetic, plus a null-`DateApplied` guard on the `Applied` branch — a null must fail safe rather than read as "infinitely old" and auto-ghost the job. Covered by `RulesEngineProspectTests`. +- **`StageAnalytics`** already filtered to `PipelineCategory.Active`, so prospects drop out of time-in-stage automatically. + +### Deliberate behaviour changes + +- `DaysSince` is now `int?`, null for prospects. Returning `0` would render as "Applied 0 days ago" — a lie. The API DTO and the frontend `JobApplication` type follow; the UI renders `—`. +- Applied-volume analytics and average-days-since-applied now filter `DateApplied != null`, so prospects cannot drag the average toward zero or inflate applied counts. +- `JobFlowBar` omits the "Applied" milestone entirely when there is no applied date. +- Custom (non-canonical) statuses are **not** treated as prospects. They predate this split and have always counted as applied; assuming otherwise would silently drop them out of existing users' analytics. + +### Costs accepted + +- `JobApplication` temporarily carries both `JobId` and its own opportunity columns — real duplication, time-boxed to the Phase 1 cutover. +- An empty `Jobs` table ships. Preferred over a backfill that no code consumes and that a later, better-informed cutover might shape differently. + +## Migration notes (read before the next migration) + +`20260717071417_AddJobEntityAndProspectStages` was **hand-edited after scaffolding**. `dotnet ef migrations add` additionally emitted `CreateTable` for `TrustedDevices`, `TwoFactorRecoveryCodes`, `UserSessions` and `AddColumn` for six `AspNetUsers` columns (`Microsoft*`, `Totp*`). + +Those tables **already exist in every real database** — they were provisioned by the idempotent reconciler in `StartupInitializationExtensions`, not by a migration, so the prior `ModelSnapshot` did not know them and the scaffolder diffed them as missing. Verified directly against the live dev database: all three tables are present. Leaving the scaffolded statements in would have failed the deploy with "table already exists". They were removed; the reconciler still creates them on a fresh boot via `CREATE TABLE IF NOT EXISTS`. + +`IX_JobApplications_OwnerUserId_IsDeleted_Status` was removed for the same reason: the reconciler applies it, and MySQL needs a `Status(50)` prefix length that the scaffolded DDL does not carry (see the comment in `JobTrackerContext.OnModelCreating`). + +**The regenerated snapshot now includes those tables, so future migrations will not re-scaffold them.** This ADR's migration is the one that closes that drift. + +EF emits a warning that the `SavedAt` backfill `UPDATE` runs while a rebuild of `JobApplications` is pending. Verified empirically against a copy of the real dev database (13 rows): the migration applies cleanly, all 36 pre-existing columns survive the rebuild, `DateApplied` values are preserved, `SavedAt` backfills correctly with no `0001-01-01` sentinels remaining, and `DateApplied` ends up nullable. + +## Phase 1 cutover plan + +1. Dual-write `Job` on every create path (`JobApplicationsController.Create`, `GmailController`'s job creation, CSV import). +2. Backfill one `Job` per existing `JobApplication`; link via `JobId`. +3. Flip reads to `Job`, one endpoint at a time. +4. Drop the duplicated opportunity columns from `JobApplication`. +5. Make `JobId` non-nullable. + +A `Job` with no `JobApplication` row is the eventual clean representation of a saved-but-not-applied job. Until step 4, the Prospect stages on `JobApplication.Status` carry that meaning instead. + +## Alternatives considered + +- **Full split in Phase 0.** Rejected: touches 38 endpoints, the Kanban, the table, the rules engine and the wizard at once, with no staging environment and a straight-to-prod deploy. The discovery report ranked this the highest-risk item in the plan. +- **Prospect stages only, no `Job` entity.** Would have unblocked the workflow, but leaves the duplication that makes applying twice to one role copy the whole description, and defers the schema foundation the roadmap's Phase 3/4 depend on. +- **Keep `DateApplied` non-nullable, add `SavedAt` alongside.** Rejected: a saved job would still carry a fabricated applied date — precisely the defect this ADR exists to remove. diff --git a/docs/documentation-rules.md b/docs/documentation-rules.md new file mode 100644 index 0000000..4ef5373 --- /dev/null +++ b/docs/documentation-rules.md @@ -0,0 +1,3 @@ +# documentation-rules + +TODO: Complete documentation. diff --git a/docs/implementation-roadmap.md b/docs/implementation-roadmap.md new file mode 100644 index 0000000..f738f07 --- /dev/null +++ b/docs/implementation-roadmap.md @@ -0,0 +1,227 @@ +# Jobjakt — Implementation Roadmap + +Date: 2026-07-17 · **Updated after Phase 0** (see `docs/phase-0-foundation-report.md`) +Companion to `docs/application-discovery-report.md`. Every task below traces to a verified finding there. + +> **Phase 0 is complete.** Architecture docs restored, **AI sidecar locked down to backend-only and verified live**, pipeline expanded beyond `Applied` (10 stages, 3 board groups), `Job` entity introduced. Phase 1 below has been re-scoped against the *actual* architecture rather than the assumptions the original plan carried. Product decisions from 2026-07-17 are folded in throughout. +> +> **Product decisions now settled** (were open questions): +> 1. **Career profile storage** — relational for Experience/Education/Skills/Projects; JSON blob for the long tail. *Phase 3 is unblocked.* +> 2. **AI providers** — fix the docs to match the code; do **not** build the multi-provider abstraction. *Task 5.1 is now S, not L.* +> 3. **Job discovery order** — manual URL import → browser extension → official APIs. **Scraping is not a starting point.** *Phase 6 re-ordered.* +> 4. **Geography** — Norway first, but **no hardcoding Norway**; market is a data dimension. *New task 6.6, and `Job.CountryCode`/`Job.Source` already exist.* +> 5. **Free vs Premium** — Free: job tracking, basic career profile, basic CV. Premium: advanced AI, more themes, automation, analytics, more storage. **Not count-based.** *Phase 7 scoped.* +> 6. **Mockups** — `F:\Pictures\website\jobtracker\new` is the source of truth **only where a mockup exists**; do not invent pages from them. + +**Legend** +Priority: `P0` blocker · `P1` high · `P2` medium · `P3` later +Difficulty: `XS` <½day · `S` ~1day · `M` 2–4days · `L` ~1–2wk · `XL` 2wk+ + +**Ordering principle:** the guide names "Excellent Job Tracking" and "Excellent Application Workflow" as goals #1 and #2. Phase 1 exists because the data model currently cannot represent goal #2 at all. Nothing in Phases 3–7 is worth building before that is true. + +--- + +## Phase 0 — Foundation corrections ✅ DONE (2026-07-17) + +Delivered: architecture docs restored and corrected; AI sidecar authenticated + de-published; `JobPipeline` gained `Saved`/`Interested`/`Preparing` under a new `Prospect` category; `DateApplied` nullable + `SavedAt` added; `Job` entity introduced additively; ADR-002 written. Full record in `docs/phase-0-foundation-report.md`. + +Original tasks 1.1, 1.2, 1.3 (backend half), 1.5, 1.7, 1.8, 1.9, 1.10 are complete and removed from Phase 1 below. + +--- + +## Phase 1 — Critical fixes (re-scoped after Phase 0) + +Goal: finish surfacing the pre-application workflow in the UI, and close the security findings that need an operator. + +| # | Task | Priority | Difficulty | Dependencies | Expected value | +|---|---|---|---|---|---| +| 1.1 | **Surface prospect stages in the UI** — add `Saved`/`Interested`/`Preparing` to `src/pipeline.ts` (`PIPELINE_STATUSES`), the Kanban columns, the status filter and the status menus. | **P0** | **M** | Phase 0 | **The backend supports the workflow; the UI does not yet expose it.** Phase 0 deliberately changed no UI behaviour. Until this lands, users still cannot save an unapplied job. Note the Kanban already has 6 columns — 9 stages needs a layout decision. | +| 1.2 | **Point the add-job wizard at `Saved`/`Preparing`** | **P0** | **XS** | 1.1 | Completes the workflow end to end. The 6-step wizard already exists (`components/AddJobModal.tsx:368`); this is a default-value change once 1.1 makes the stage selectable. | +| 1.3 | **Drag-and-drop on the Kanban** | **P2** | **M** | 1.1 | Board is drop-target-only today. Table stakes at every competitor. Deferred: not blocking. | +| 1.4 | **Rotate DataProtection keys** | **P1** | **XS** | none | **Needs an operator — cannot be done from here.** Keys remain recoverable from git history (`519c32e`, `955cae6`). Open since 2026-07-03. | +| 1.5 | **Fix the CORS wildcard landmine** — `Cors:Origins="*"` triggers `SetIsOriginAllowed(_ => true)` **with** `AllowCredentials()` (`Program.cs:96-102`): any site could make authenticated requests with the user's session cookie. Not currently active (compose never sets `Cors__Origins`), but it is one config value away. Reject the `*`+credentials combination outright. | **P1** | **XS** | none | **Found during Phase 0.** Cheap fix, severe if ever triggered. Was out of Phase 0's 5-item scope. | +| 1.6 | **Resolve the dead `careerView` prop** — either wire it or remove the tab | **P1** | **XS** | none | Ships a visible "CV Builder" tab that does nothing (`views/ProfilePage.tsx:231`). Removing it until Phase 4 is honest; leaving it is not. **Both files are uncommitted WIP — confirm intent before touching.** | +| 1.7 | **Fix the onboarding CV check** — read the structured profile, not `profileCvText` | **P2** | **XS** | none | `components/OnboardingChecklist.tsx:31` tells users with a parsed profile to upload a CV again. | +| 1.8 | **Fix the 5 pre-existing frontend test failures** in `profile-page.test.tsx` and `settings-view.test.tsx` | **P1** | **S** | none | **Failing on `main` before Phase 0** (verified by stashing all changes and re-running). CI runs the whole suite, so `main` is red — every future change lands on a broken baseline. | +| 1.9 | **Decide what to do with the `feature/career-workspace` branch.** ✅ *Investigated 2026-07-17 — see below.* The orphan tables are not an abandoned prototype: they are the local footprint of **10 unmerged commits** (local + `origin`, last touched 2026-07-12) containing working, tested code and 1,100 lines of research/strategy. All 7 tables are **empty** in dev, so there is no data to migrate. Branch is **29 commits behind main**. | **P0** | **M** | none | **Blocks Phases 3 and 4 from being planned honestly.** Re-deriving this work would waste weeks; ignoring it leaves a rebase debt that grows every day main moves. See `docs/career-workspace-branch-assessment.md`. | +| 1.10 | **Dev DB is missing 2 migrations** (`AddCorrespondenceEmailFields`, `AddShortSummary`) yet has `SyncModelSnapshot` — the reconciler patched the columns instead. | **P2** | **S** | none | Local-only inconsistency, but it means the dev DB is not a faithful rehearsal for a prod migration. Worth reconciling before the Phase 1 cutover. | + +**Phase 1 exit:** a user can save a job they have not applied to, walk the wizard, prepare materials, and *then* mark it applied — visibly, in the UI. CI is green. + +--- + +## Phase 2 — UX improvements + +Goal: the guide's "users should always understand where they are, what they can do, what happens next." + +| # | Task | Priority | Difficulty | Dependencies | Expected value | +|---|---|---|---|---|---| +| 2.1 | **Split `/profile` and `/career`** — Profile = identity + security; Career = career data. Stop rendering one 1368-line component behind a boolean. | **P1** | **M** | none | Fixes the guide's "everything should have one obvious place". Prerequisite for Phase 3 — the Career Workspace cannot be built inside `ProfilePage`. | +| 2.2 | **Decompose `ProfilePage.tsx` (1368 lines)** into profile / CV / security sections | **P1** | **M** | 2.1 | Worst file in the frontend. Blocks Phase 3/4 work. | +| 2.3 | **Real onboarding flow** — `Signup → Verify → Profile → Import CV → Connect email → First job` replacing the 2-item checklist | **P1** | **M** | 2.1 | Currently a dismissible checkbox pair. **Surface the Gmail connect step** — the strongest differentiator is buried in `/settings/connected-accounts`. | +| 2.4 | **Dedicated `/register` screen** | **P2** | **S** | none | Endpoint exists but returns 403 by default and has no route; signup is hidden inside `LoginPage`. Required for Phase 7; harmless now. | +| 2.5 | **Introduce a server-cache layer** (React Query or equivalent) | **P2** | **M** | none | Root cause of the 600–1400-line components and the hand-rolled `refreshToken` prop-threading. Pays for itself across Phases 3–6. Adopt incrementally, not as a rewrite. | +| 2.6 | **Decompose `JobDetailsDialog.tsx` (1400 lines)** | **P2** | **M** | 2.5 | A dialog carrying an entire workspace. | +| 2.7 | **Interview prep hub screen** | **P2** | **S** | none | `/jobapplications/{id}/interview-prep` already works and is invisible. Cheap win — a shipped feature nobody can reach. | +| 2.8 | **Retire `react-scripts` as test runner** (move to Jest+SWC or Vitest) | **P2** | **M** | none | Removes one of three frontend toolchains. Do not touch the router in the same change. | +| 2.9 | **Expand analytics** — funnel, response rate, time-in-stage | **P3** | **M** | 1.3 | The paid feature at Teal/Huntr. Needs correct prospect-vs-applied accounting from 1.3 first. | + +--- + +## Phase 3 — Career Workspace + +Goal: one professional source of truth that can actually feed outputs. + +| # | Task | Priority | Difficulty | Dependencies | Expected value | +|---|---|---|---|---|---| +| 3.1 | **Model Experience/Education/Skills/Projects relationally**; keep the long tail (awards, publications, organisations, references, custom sections) as a JSON blob. **Decided 2026-07-17** — no longer an open question. | **P1** | **L** | 1.9 | Buys queryable skills (needed for real matching + keyword gaps), section-level history, and concurrent-edit safety. Hybrid avoids a full rewrite of `StructuredCvProfile`. **Check task 1.9 first** — orphan `CareerProfiles`/`CvVersions` tables suggest this was attempted before. | +| 3.2 | **Add the missing profile sections** — Awards, Publications, Organisations, References | **P1** | **S** | 3.1 | The guide names them; `StructuredCvProfile` has no home for them beyond generic `OtherSections`. These are the blob half of 3.1. | +| 3.3 | **Real Career Workspace page** — replace the 36-line tab facade | **P1** | **M** | 2.1, 2.2 | Currently a wrapper around `ProfilePage`. | +| 3.4 | **Separate Career Profile from Master CV** | **P1** | **M** | 3.1 | The glossary is explicit — "The career profile is NOT a CV"; Master CV is a *generated representation*. Code has one blob. Getting this wrong makes Phase 4 impossible. | +| 3.5 | **CV variants** (Software Engineer CV / Management CV) | **P2** | **M** | 3.4 | In the glossary; no code. Distinct from per-application `TailoredCvDraft`, which works correctly and must not be disturbed. | +| 3.6 | **Retention policy for `CvExtractionRun`** | **P2** | **S** | none | Three copies of every CV (raw/normalized/structured), unbounded. | +| 3.7 | **Move avatars out of the DB column** | **P3** | **S** | none | Base64 blob on the `/auth/me` hot path. | + +**Do not disturb:** `TailoredCvDraft` correctly implements "the master CV must never be modified automatically" — the single most important documented invariant, and it already holds. + +--- + +## Phase 4 — CV Builder + +Goal: `Content Tab → Customise Tab → Preview → Export` (guide `:312`). +**This is the largest item in the plan.** The existing inert tab makes it look nearly done; it is not started. + +| # | Task | Priority | Difficulty | Dependencies | Expected value | +|---|---|---|---|---|---| +| 4.1 | **Design the `CvTheme` model** — layout, columns, header position, font family, base size + per-element deltas, spacing, margins, accent + application targets, icon style, photo settings | **P1** | **M** | 3.4 | **The keystone.** Everything else in Phase 4 depends on themes being *data*. Modelled on FlowCV's proven control set (report §7), trimmed to ~12 controls per the guide's "avoid excessive configuration". | +| 4.2 | **Replace `CvTemplateRenderer` with one parameterized renderer** | **P1** | **L** | 4.1 | Current code is a C# `switch` over 6 hardcoded HTML-string functions with `roundedPhoto`/`curvedHeader` booleans (`Services/CvTemplateRenderer.cs:22`). **A structural dead end — do not extend it.** | +| 4.3 | **Seed 3–5 themes as theme documents** — ATS Professional, Modern Professional, Creative | **P1** | **M** | 4.2 | The guide's explicit target. Cheap once 4.1/4.2 land; impossible before. | +| 4.4 | **Content tab** — section add/remove/reorder, entry editing, drag-and-drop **with keyboard support** | **P1** | **L** | 3.4 | FlowCV's keyboard drag affordances are worth matching (report §7). | +| 4.5 | **Customise tab** — the ~12 controls from 4.1 | **P1** | **M** | 4.1, 4.2 | Currently **nothing** is customisable. | +| 4.6 | **Live client-side preview** | **P1** | **L** | 4.2 | Today: server round-trip. FlowCV: continuous, side-by-side. Hardest piece — the renderer must run client-side or stream fast enough to feel live. | +| 4.7 | **Wire export into the builder** | **P2** | **S** | 4.6 | `POST /profile-cv/export-pdf` + Playwright already work. Make Download persistent, not a mode. | +| 4.8 | **Split `ProfileCvController` (2249 lines)** | **P2** | **M** | — | Do it while working here, not as a standalone refactor. | +| 4.9 | ~~Research Reactive Resume / Novoresume / ElegantCV~~ ✅ **ALREADY DONE** — on `feature/career-workspace`: `docs/cv-builder-competitor-deep-research.md` (327 lines, 8 teardowns incl. Novoresume + Reactive Resume, feature matrix, business-model analysis). **Recover it; do not redo it.** | **P1** | **XS** | 1.9 | Its conclusions independently match this plan's §7/§10 reasoning — structured-form + live preview beats canvas; client-side preview is a hard requirement; themes must be declarative data. It also carries the pricing intelligence Phase 7 needs (Resume.io's F BBB rating for billing traps; Novoresume blocking re-download of already-paid CVs), which independently supports the "never gate on count" decision. | + +--- + +## Phase 5 — AI improvements + +Goal: polish. This is the healthiest area — grounding in the structured profile is already right, and "AI never has final control" already holds. + +| # | Task | Priority | Difficulty | Dependencies | Expected value | +|---|---|---|---|---|---| +| 5.1 | **Fix `docs/00-ai-context.md` to match the code.** **Decided 2026-07-17: do NOT build the abstraction.** | **P1** | **S** | none | The doc describes a provider interface over OpenAI/Gemini/Claude/Ollama with admin control and per-user choice. Reality: one `AI_PROVIDER` env var over Ollama/Gemini/Groq. Multi-provider cloud AI also undermines the privacy moat (see `docs/research/competitors.md` §4). Revisit only if a customer asks. `docs/architecture/current.md` §9 already records the truth. | +| 5.2 | **AI usage metering** | **P1** | **M** | 1.5 | No quota, no tracking, no ceiling. Hard blocker for Phase 7; a cost risk today with `AI_PROVIDER=gemini`. | +| 5.3 | **Surface CV generation inside the add-job wizard** | **P2** | **S** | 1.4 | The target workflow says "Generate CV if needed" at step 3. `POST /generate-tailored-cv-draft` exists but only post-save. | +| 5.4 | **Keyword-gap analysis on match score** | **P2** | **M** | none | `JobCvMatchService` + `/match-score` exist. Gap analysis is the specific thing people pay Jobscan $49.95/mo for. | +| 5.5 | **Write ADR-004 (AI provider system)** | **P2** | **S** | 5.1 | 0-byte file naming a real decision. | + +--- + +## Phase 6 — Job discovery + +Goal: help users find opportunities. **Zero code exists.** Explicitly an enhancement — the guide: "Job discovery supports tracking. It does not replace job boards." + +**Order decided 2026-07-17:** (a) manual URL import → (b) browser extension → (c) official APIs where available. **Scraping is explicitly not the starting point.** This reorders the original plan: the extension now comes *before* a search backend. + +| # | Task | Priority | Difficulty | Dependencies | Expected value | +|---|---|---|---|---|---| +| 6.1 | **(a) Harden manual URL import** — `POST /jobimport/preview` is preview-only (a 27-line controller, no persistence, no import history, no dedup at import time). | **P2** | **M** | Phase 0 | Step (a) of the decided order and **already mostly built**. Cheapest discovery win; the wizard already calls it. | +| 6.2 | **(b) Browser extension / bookmarklet capture** — reuse `jobimport/preview`; land captures in `Saved`. | **P2** | **L** | 6.1, 1.1 | Step (b). Teal/Huntr/Simplify all ship one; Jobjakt is server-side parse only. **Kills the single biggest friction (manual entry)** and needs no search backend. Start with a bookmarklet — a full Chrome-store extension is high maintenance. | +| 6.3 | **(c) Official job-board APIs where available** | **P3** | **L** | 6.2, 6.6 | Step (c). Legal and low-maintenance where an API exists. | +| 6.4 | **Search UI + filters** (title, location, remote, industry) | **P3** | **M** | 6.3 | Only worth it once (c) supplies real data. | +| 6.5 | **One-click import into tracker** | **P3** | **S** | 6.3 | Lands in `Saved` — which exists as of Phase 0. | +| 6.6 | **Make market a data dimension, not an assumption** — Finn/NAV/Jobbnorge/LinkedIn are Norway-specific. **Decided: Norway first, but no hardcoding Norway.** | **P2** | **M** | none | `Job.CountryCode` and `Job.Source` already exist (added in Phase 0) — the entity is ready. The *plugins*, locale and currency handling still assume NO. Doing this before 6.3 stops each new market being a rewrite. | +| 6.7 | **Explicitly out of scope: scraping.** | — | — | — | Recorded so it is not re-proposed. Also out: auto-apply bots (ToS/ethics/quality; contradicts "apply to more *suitable* jobs"). | + +--- + +## Phase 7 — SaaS preparation + +Goal: commercialise. Last, per the guide's "do not over-engineer before needed. Excellent UX is more important." +**Good news:** the hard part — multi-tenancy — is already done and tested. Everything below is additive. + +**Tiers decided 2026-07-17.** Free: job tracking, basic career profile, basic CV. Premium: advanced AI, more themes, automation, analytics, more storage. + +**The gate is capability, never count.** No CV-count or job-count caps — that is Huntr's 100-job limit, Teal's AI credits and FlowCV's 1-resume limit, i.e. the exact "free tier caps at the point of seriousness" frustration in `docs/research/competitors.md` §1.4. Jobjakt's moat is privacy + self-hosting + free local AI; a count-based paywall surrenders the moat while inheriting the complaint. + +| # | Task | Priority | Difficulty | Dependencies | Expected value | +|---|---|---|---|---|---| +| 7.1 | **Open registration + CAPTCHA** | **P2** | **M** | 2.4, 7.3 | Registration is 403 by default; **no CAPTCHA exists** (verified). Rate limiting alone is not enough for public signup. | +| 7.2 | **Plan / tier / entitlement model** — capability flags (`advancedAi`, `premiumThemes`, `automation`, `analytics`, `storageBytes`), not counters. | **P3** | **M** | none | No concept of a plan exists anywhere. Shape it around the decided split so the free tier stays genuinely useful. | +| 7.3 | **Usage quotas — AI + storage only** | **P3** | **M** | 5.2, 7.2 | **Do not open registration before this lands.** AI and storage are unmetered and unbounded; these are real cost, so they are legitimate limits. Job/CV counts are not. | +| 7.4 | **Storage limits + attachment caps** | **P3** | **S** | 7.2 | The "more storage" premium lever. | +| 7.5 | **Stripe billing** | **P3** | **L** | 7.2 | Still blocked on **Stripe keys** — the only remaining hard blocker. Tiers are now decided. | +| 7.6 | **Public CV** (`/cv/{guid}`) | **P3** | **M** | 3.4, 4.2 | Documented in `docs/00-ai-context.md`; **zero code** — no route, no `IsPublic`, no slug. Privacy-first random GUID, no usernames. | +| 7.7 | **Premium themes** | **P3** | **S** | 4.3, 7.2 | Trivial once themes are data. Impossible while they are C# methods. A decided premium lever. | +| 7.8 | **Dependency CVE scanning in CI** | **P2** | **S** | none | CI explicitly disables audit (`npm_config_audit: 'false'`). No vulnerability scan runs anywhere. | +| 7.9 | **Per-user AI provider cost controls** | **P3** | **S** | 5.2, 7.2 | With `AI_PROVIDER=gemini` the "advanced AI" tier spends real money per call. Metering (5.2) measures; this enforces. | + +--- + +## Critical path + +``` +[Phase 0 DONE] pipeline stages · DateApplied nullable · Job entity · sidecar secured · docs restored + +1.1 Prospect stages in UI ── 1.2 Wizard target (finishes the workflow users can see) +1.8 Fix red CI (independent — do first; everything lands on this baseline) +1.4 Rotate DP keys · 1.5 CORS fix (independent security) +1.9 Orphan tables ── informs 3.1 + +2.1 Split profile/career ── 2.2 Decompose ProfilePage ── 3.3 Career Workspace + └─ 3.4 Profile ≠ Master CV ──┬─ 4.1 CvTheme model ── 4.2 Renderer ──┬─ 4.3 Themes + │ ├─ 4.5 Customise tab + │ └─ 4.6 Live preview ── 4.7 Export + └─ 4.4 Content tab +4.9 Reactive Resume research ── must precede 4.1 +6.6 Market as data ── 6.3 Official APIs +5.2 AI metering ── 7.3 Quotas ── 7.1 Open registration +``` + +**Two things now gate everything:** `3.4` (profile ≠ CV) and `4.1` (themes as data). The third — the workflow — was unblocked by Phase 0; only its UI surface (1.1) remains. + +--- + +## Effort summary + +| Phase | Rough size | Note | +|---|---|---| +| 0 — Foundation | **done** | Delivered 2026-07-17. | +| 1 — Critical fixes | ~3–5 days | Shrunk: Phase 0 absorbed most of it. Mostly UI surfacing + two operator tasks. | +| 2 — UX | ~2–3 wk | 2.5 (data layer) is the multiplier. | +| 3 — Career Workspace | ~2–3 wk | Grew slightly: relational profile (3.1) is now L, not a coin-flip. | +| 4 — CV Builder | **~4–6 wk** | The big one. Do not underestimate because a tab exists. | +| 5 — AI | ~1 wk | Shrunk: 5.1 is a docs fix, not an abstraction build. | +| 6 — Job discovery | ~2–3 wk | Shrunk: no scraping, and (a) is mostly built. The extension (6.2) carries most of the value. | +| 7 — SaaS | ~3–4 wk | Tiers decided; needs Stripe keys. | + +--- + +## Product decisions — settled 2026-07-17 + +All six of the original blocking questions are answered. Recorded here so they are not re-litigated. + +| # | Question | Decision | Effect | +|---|---|---|---| +| 1 | Career profile storage | **Relational** for Experience/Education/Skills/Projects; **JSON blob** for the long tail | Phase 3 unblocked; 3.1 is now a task, not a question | +| 2 | AI providers | **Fix the docs, do not build the abstraction** | 5.1 drops from L to S | +| 3 | Job discovery | **(a) manual URL import → (b) browser extension → (c) official APIs. No scraping.** | Phase 6 reordered; extension promoted ahead of a search backend | +| 4 | Geography | **Norway first, no hardcoding Norway** | New task 6.6; `Job.CountryCode`/`Source` already added in Phase 0 | +| 5 | Free vs Premium | Free: tracking, basic profile, basic CV. Premium: advanced AI, more themes, automation, analytics, more storage. **Capability-gated, never count-gated** | Phase 7 scoped; 7.2/7.3 reshaped | +| 6 | Mockups | Source of truth **only where a mockup exists**; do not invent pages from them | Phases 2 and 4 constrained | + +## Remaining blockers + +Only two things now block work, and both need you rather than a decision: + +1. **Stripe keys** (gates 7.5). Tiers are decided; the integration needs credentials. +2. **DataProtection key rotation** (task 1.4). Needs an operator with production access. + +## Open questions raised *by* Phase 0 + +Not product decisions — engineering findings that need a call before the phase they touch: + +1. **Orphan DB tables** (task 1.9). The dev database carries `CareerProfiles`, `CareerProfileVersions`, `CvVariants`, `CvVersions`, `TailoredApplications`, `InterviewPrepNotes`, `AiWorkspaceNotes` — in no model, no migration. An abandoned Career Workspace attempt. **Was there a previous design worth recovering, or is this dead weight to drop?** Answer before Phase 3 re-treads the same ground. +2. **Kanban column count** (task 1.1). The pipeline now has 9 stages; the board is built for 6. Do the three Prospect stages get their own columns, collapse into one "Not applied" column, or sit behind a toggle? A UI decision, needed to finish Phase 1. diff --git a/docs/jobs/adding-jobs.md b/docs/jobs/adding-jobs.md new file mode 100644 index 0000000..bbb5556 --- /dev/null +++ b/docs/jobs/adding-jobs.md @@ -0,0 +1,74 @@ +# Adding Jobs + +## Goal + +Adding a job should be fast. + +Users should not fill unnecessary forms. + +--- + +# Preferred Flow + +## Option 1: Import URL + +User pastes job advert URL. + +System: + +1. Fetch page. +2. Extract details. +3. Create draft job. +4. Show review screen. + +--- + +# Review + +User confirms: + +- Title. +- Company. +- Location. +- Description. +- Requirements. + +--- + +# Option 2: Manual Entry + +Fallback when import fails. + +Only request: + +Required: + +- Job title. +- Company. +- URL or description. + +Optional: + +- Salary. +- Location. +- Notes. + +--- + +# Company Handling + +If company exists: + +Link automatically. + +If company does not exist: + +Create automatically. + +Do not force a separate company creation flow. + +--- + +# UX Principle + +Adding a job should take seconds, not minutes. \ No newline at end of file diff --git a/docs/jobs/analytics.md b/docs/jobs/analytics.md new file mode 100644 index 0000000..47b07da --- /dev/null +++ b/docs/jobs/analytics.md @@ -0,0 +1,29 @@ +# Job Search Analytics + +## Purpose + +Help users understand their search. + +--- + +# Possible Metrics + +Applications: + +- Total. +- Monthly. +- By industry. + +Progress: + +- Interview rate. +- Response rate. +- Success rate. + +--- + +# Rules + +Analytics should provide insight. + +Avoid vanity metrics. \ No newline at end of file diff --git a/docs/jobs/applications.md b/docs/jobs/applications.md new file mode 100644 index 0000000..d14164a --- /dev/null +++ b/docs/jobs/applications.md @@ -0,0 +1,3 @@ +# applications + +TODO: Complete documentation. diff --git a/docs/jobs/communication-tracking.md b/docs/jobs/communication-tracking.md new file mode 100644 index 0000000..0f89e26 --- /dev/null +++ b/docs/jobs/communication-tracking.md @@ -0,0 +1,49 @@ +# Communication Tracking + +## Purpose + +Store application history. + +--- + +# Sources + +Manual: + +- Notes. +- Calls. +- Messages. + +Automatic: + +- Email integrations. + +--- + +# Email Providers + +Supported: + +- Gmail. +- Microsoft. +- IMAP. + +--- + +# Storage + +Communication should belong to: + +Application + +or + +Company + +depending on context. + +--- + +# Privacy + +Users control connected accounts. diff --git a/docs/jobs/company-management.md b/docs/jobs/company-management.md new file mode 100644 index 0000000..3afd16f --- /dev/null +++ b/docs/jobs/company-management.md @@ -0,0 +1,58 @@ +# companies + +TODO: Complete documentation. +# Company Management + +## Purpose + +Companies should be reusable objects. + +--- + +# Features + +Store: + +- Name. +- Website. +- Location. +- Industry. +- Notes. + +--- + +# Relationships + +One company: + +↓ + +Many jobs + +↓ + +Many applications + +--- + +# Automatic Creation + +When importing jobs: + +If company exists: + +Reuse. + +If not: + +Create automatically. + +--- + +# Future + +Possible: + +- Company research. +- Reviews. +- Hiring insights. \ No newline at end of file diff --git a/docs/jobs/followups.md b/docs/jobs/followups.md new file mode 100644 index 0000000..32fb118 --- /dev/null +++ b/docs/jobs/followups.md @@ -0,0 +1,3 @@ +# followups + +TODO: Complete documentation. diff --git a/docs/jobs/future-features.md b/docs/jobs/future-features.md new file mode 100644 index 0000000..b6da3de --- /dev/null +++ b/docs/jobs/future-features.md @@ -0,0 +1,51 @@ +# Future Job Features + +Possible ideas: + +## Advanced Search + +- Job recommendations. +- Saved searches. +- Alerts. + +--- + +## AI Matching + +Analyse: + +- User skills. +- Job requirements. + +Suggest: + +- Match score. +- Missing skills. +- Improvement areas. + +--- + +## Interview Management + +Support: + +- Preparation. +- Questions. +- Notes. +- Feedback. + +--- + +## Salary Tracking + +Potential: + +- Salary expectations. +- Negotiations. +- Market data. + +--- + +# Priority Rule + +Only build features that improve the user's ability to find and secure employment. \ No newline at end of file diff --git a/docs/jobs/job-import.md b/docs/jobs/job-import.md new file mode 100644 index 0000000..fd8d780 --- /dev/null +++ b/docs/jobs/job-import.md @@ -0,0 +1,50 @@ +# Import System + +## Purpose + +Reduce manual entry. + +--- + +# Supported Imports + +## Job Import + +Sources: + +- URL. +- API. +- Manual. + +--- + +## CV Import + +Sources: + +- PDF. +- DOCX. + +Process: + +File + +↓ + +Extraction + +↓ + +Structured data + +↓ + +User review + +--- + +# Validation + +Never silently create incorrect information. + +Show extracted data before saving. \ No newline at end of file diff --git a/docs/jobs/job-lifecycle.md b/docs/jobs/job-lifecycle.md new file mode 100644 index 0000000..a1f36f0 --- /dev/null +++ b/docs/jobs/job-lifecycle.md @@ -0,0 +1,91 @@ +# Job Lifecycle + +## Purpose + +Defines how a job progresses through Jobjakt. + +--- + +# Discovery + +User finds opportunity. + +Sources: + +- Manual entry. +- Job URL. +- Future job search integrations. + +Status: + +Interested. + +--- + +# Preparation + +User decides to apply. + +Actions: + +- Review job details. +- Prepare CV. +- Prepare cover letter. +- Gather documents. + +Status: + +Preparing. + +--- + +# Application + +User submits application. + +Track: + +- Date applied. +- Documents used. +- Method. + +Status: + +Applied. + +--- + +# Interview + +Track: + +- Interview dates. +- Interview type. +- Notes. +- Preparation. + +Status: + +Interview. + +--- + +# Outcome + +Possible states: + +Offer. + +Rejected. + +Withdrawn. + +Accepted. + +--- + +# Important + +The workflow should remain flexible. + +Different industries have different hiring processes. \ No newline at end of file diff --git a/docs/jobs/job-search.md b/docs/jobs/job-search.md new file mode 100644 index 0000000..a21f644 --- /dev/null +++ b/docs/jobs/job-search.md @@ -0,0 +1,55 @@ +# Job Search Enhancement + +## Purpose + +Help users discover opportunities. + +This is not the primary product. + +--- + +# Ideal Flow + +Search jobs + +↓ + +Find interesting role + +↓ + +Import into Jobjakt + +↓ + +Track application + +--- + +# Potential Filters + +- Job title. +- Keywords. +- Location. +- Remote. +- Industry. +- Salary. +- Experience level. + +--- + +# Integrations + +Research available: + +- Free APIs. +- Public feeds. +- Job providers. + +--- + +# Rules + +Do not become another job board. + +The value is organisation. \ No newline at end of file diff --git a/docs/jobs/overview.md b/docs/jobs/overview.md new file mode 100644 index 0000000..a955ded --- /dev/null +++ b/docs/jobs/overview.md @@ -0,0 +1,141 @@ +# overview + +TODO: Complete documentation. +# Job Tracking Overview + +## Purpose + +Job tracking is the primary purpose of Jobjakt. + +The application exists first to help users manage their job search. + +Career features, CV generation, AI assistance, and job discovery support this workflow. + +They do not replace it. + +--- + +# Core Product Statement + +Jobjakt helps users: + +Find opportunities + +↓ + +Track applications + +↓ + +Manage communication + +↓ + +Prepare better applications + +↓ + +Secure employment + +--- + +# Core Objects + +The main entities are: + +## Job + +The opportunity itself. + +Examples: + +- Software Engineer position. +- Developer role. +- Marketing position. + +Contains: + +- Title. +- Company. +- Description. +- URL. +- Location. +- Salary information. +- Requirements. + +--- + +## Application + +The user's attempt to obtain the role. + +A job can exist without an application. + +Example: + +User saves an interesting role. + +Later they apply. + +--- + +## Company + +The organisation offering the role. + +Should be reusable. + +Example: + +Multiple jobs from Google should link to one company. + +--- + +## Communication + +Interactions related to the application. + +Examples: + +- Emails. +- Calls. +- Messages. +- Notes. + +--- + +## Follow-up + +A planned action. + +Examples: + +- Contact recruiter. +- Check application status. +- Prepare interview. + +--- + +# Product Principles + +## Reduce manual work + +Import information where possible. + +--- + +## Keep users organised + +The user should always know: + +- What jobs exist. +- What stage applications are at. +- What action is next. + +--- + +## Avoid unnecessary complexity + +Do not require users to complete every field. + +Smart defaults are preferred. \ No newline at end of file diff --git a/docs/phase-0-foundation-report.md b/docs/phase-0-foundation-report.md new file mode 100644 index 0000000..49f18b2 --- /dev/null +++ b/docs/phase-0-foundation-report.md @@ -0,0 +1,253 @@ +# Phase 0 — Foundation Corrections Report + +Date: 2026-07-17 +Scope: the five priorities set after the discovery review. Nothing else was touched. +Companion to `docs/application-discovery-report.md`, `docs/architecture/current.md`, `docs/decisions/ADR-002-job-application-model.md`. + +**Nothing was committed.** All changes are in the working tree for review. + +--- + +## 1. Completed + +### Priority 1 — Restore accurate architecture documentation ✅ + +The active `docs/` tree was a scaffold: 139 files, median ~480 bytes, while the real documentation sat archived. `docs/AI_SESSION_START.md` sends every future session to read those stubs, so each one started from fiction. + +- **`docs/architecture/current.md`** — restored from `docs/_archive/SYSTEM_OVERVIEW.md` (20 KB) and re-verified line by line against the code. Was a 3-line "TODO: Complete documentation." stub. **Nine corrections** to the archived original: + + | Archived claim (2026-07-02) | Reality (verified 2026-07-17) | + |---|---| + | "CRA/react-scripts 5, TypeScript 4.9" | Next.js 16 + TypeScript 5.9; CRA survives only as the test runner | + | Root `Controller/` folder is dead code | Removed in `519c32e` | + | CI runs "a whitelist of 10 frontend test files" | Whitelist gone; whole suite runs, with a comment forbidding its return | + | Match score ❌ missing | Exists (`JobCvMatchService`, `/match-score`, `/candidate-fit`) | + | Salary is a text field | Structured (`SalaryMin`/`Max`/`Currency`/`Period`) | + | 8 migrations | 11 (after this phase) | + | ~15 controllers | 19, with exact line counts | + | Rate limiting: 2 policies | 3 (`auth-login`, `auth-email`, `auth-2fa-challenge`) | + | 6 hosted services | 7 (`DatabaseBackupHostedService` added) | + + Also documented for the first time: the three-toolchain frontend, the absence of any state-management layer, the AI provider reality vs. `docs/00-ai-context.md`'s fiction, and six historical decisions that existed only as code comments. + +- **`docs/research/competitors.md`** — restored from `docs/_archive/PRODUCT_RESEARCH.md` (13 KB, sourced). Was a stub. Six feature-matrix rows corrected against code and marked `[corrected 2026-07-17]`. The scope gap is stated plainly at the top: **Novoresume, Reactive Resume and ElegantCV are analysed nowhere in this repo**, and Reactive Resume is a Phase 4 prerequisite. + +- **`docs/decisions/ADR-002-job-application-model.md`** — written. Was a 0-byte file naming the single most consequential decision in the system. + +- **Ten 0-byte files deleted** (verified empty first): the four ADR placeholders, five `09-research/*` analyses, and `known-issues.md`. They advertised content that never existed. + +### Priority 2 — Secure the AI sidecar ✅ + +The sidecar published port 8001 to the host and had **no authentication of any kind** — the only `Authorization` header in `app.py` was outbound to Groq. Anyone reaching the host could call `/cv/rewrite`, `/summarize`, `/extract-text`: draining the Gemini/Groq API key and running arbitrary text through the model. This matters more than it looks, because `jobtracker_shared` is an *external, shared* Docker network — other containers could reach it too. + +Two layers, both required: + +1. **Network** — host port mapping removed; `expose: "8001"` only. The backend reaches it in-network at `http://ai-service:8001`. A comment tells the next person to use `docker-compose.override.yml` for local debugging rather than re-adding `ports:`. +2. **Shared secret** — `X-Ai-Service-Token` required on every endpoint except `/health` (which the backend probe and the compose healthcheck both need, and which exposes no data or generation path). Compared with `hmac.compare_digest` to avoid a timing leak. + +**Where the enforcement lives matters.** The token is unset → open, so local dev and the existing test suite keep working keyless. Production cannot reach that state: `docker-compose.yml` declares `AI_SERVICE_TOKEN=${AI_SERVICE_TOKEN:?...}`, so **the stack refuses to start without it**. Misconfiguration fails loudly at deploy rather than silently booting open at runtime. Verified: `docker compose config` with no token exits non-zero. + +The backend side is one place — all seven call sites route through the named `ai-service` `HttpClient`, so the header is set once in `Program.cs`. + +### Priority 3 — Introduce Job separate from JobApplication ✅ (foundation) + +`Job` (`Models/Job.cs`) is the opportunity; `JobApplication` is the pursuit of it. `JobApplication.JobId` is a nullable FK (`OnDelete: SetNull`). `Job` carries the same deny-on-null tenant query filter as every other owned entity. + +**Deliberately additive: nothing reads or writes `Job` yet**, no dual-write, no backfill, no legacy columns dropped. That keeps Priority 3 a pure schema change with zero behavioural risk, which is what "unblock future phases safely" asks for. The Phase 1 cutover plan (dual-write → backfill → flip reads → drop columns) is in ADR-002. + +`Job` also carries `Source` and `CountryCode`, so market is a data dimension from day one — per the "Norway first, but no hardcoding Norway" decision. The entity is ready; the *plugins* still assume NO (roadmap 6.6). + +### Priority 4 — Expand pipeline stages beyond Applied ✅ + +`JobPipeline` gained `PipelineCategory.Prospect` and three stages ahead of `Applied`: + +``` +Saved(1) · Interested(2) · Preparing(3) → Applied(4) · Waiting(5) · Interview(6) → Offer(7) → Rejected(8) · Ghosted(9) +``` + +No schema change was needed for the stages themselves — `Status` is free-text by deliberate prior design, so custom values survive. Synonyms map in (`bookmarked`/`wishlist`/`to apply` → `Saved`, `shortlisted` → `Interested`, `drafting` → `Preparing`). + +**This is what unblocks the guide's #2 priority workflow.** The 6-step add-job wizard already existed and walked users through *preparing* an application — with nowhere to put the result but `Applied` with a fabricated date. + +The supporting change: **`DateApplied` is now nullable**, with `SavedAt` added. The invariant — *`DateApplied` is set if and only if the job has left the Prospect stages* — is enforced in exactly one function, `JobPipeline.SyncAppliedDate`, called from all three status-write paths so they cannot drift. + +### Priority 5 — Update the roadmap ✅ + +`docs/implementation-roadmap.md` re-scoped against the real architecture, with all six product decisions folded in. Phase 1 shrank (~1–1.5 wk → ~3–5 days) because Phase 0 absorbed most of it; Phase 5 shrank (docs fix, not an abstraction build); Phase 6 shrank and reordered (no scraping; extension before search backend); Phase 3 grew slightly (relational profile is now a committed L). Four new tasks came out of Phase 0 findings. + +--- + +## 2. Files changed + +**Backend** +- `Models/Job.cs` *(new)* — the opportunity entity +- `Models/JobApplication.cs` — `JobId`, `SavedAt`, nullable `DateApplied`, `DaysSince` → `int?` +- `Data/JobTrackerContext.cs` — `Jobs` DbSet, tenant filter, FK config, index +- `JobTrackerApi/Services/JobPipeline.cs` — `Prospect` category, 3 stages, `IsProspect`, `SyncAppliedDate` +- `JobTrackerApi/Services/RulesEngine.cs` — prospect guard + null-`DateApplied` fail-safe +- `JobTrackerApi/Services/AnalyticsService.cs` — exclude prospects from applied-volume, average age, response-time; `SavedAt` fallback for stage entry +- `JobTrackerApi/Services/FollowUpReminderHostedService.cs` — null-safe applied date +- `JobTrackerApi/Controllers/JobApplicationsController.cs` — `SyncAppliedDate` on all 3 write paths, `SavedAt` in DTO, null-safe analytics/drafts +- `JobTrackerApi/Controllers/JobApplicationDtos.cs` — `DateApplied` → `DateTime?`, `DaysSince` → `int?`, `SavedAt` added +- `JobTrackerApi/Controllers/ExportController.cs` — null-safe CSV +- `JobTrackerApi/Program.cs` — `X-Ai-Service-Token` on the `ai-service` HttpClient + +**Frontend** (type-driven; the compiler found every site that would have rendered a null) +- `src/types.ts` — `dateApplied: string | null`, `daysSince: number | null`, `savedAt: string` +- `src/components/JobFlowBar.tsx` — omits the "Applied" milestone when there is no applied date +- `src/components/KanbanBoard.tsx` — sort falls back to `savedAt`; "Applied Nd ago" hidden when null +- `src/components/JobTable.tsx`, `JobDetailsDialog.tsx` — render `—` +- `src/components/EditJobDialog.tsx`, `ImportExportJobs.tsx` — null handling + +**AI service** +- `tools/summarizer/app.py` — token middleware +- `docker-compose.yml` — `expose` not `ports`; `AI_SERVICE_TOKEN` + `Ai__ServiceToken` with `:?` guards +- `.env.example` — `AI_SERVICE_TOKEN` documented as required + +**Tests** (+31) +- `JobTrackerApi.Tests/RulesEngineProspectTests.cs` *(new)* — 5 cases +- `JobTrackerApi.Tests/JobPipelineTests.cs` — +21 cases +- `tools/summarizer/tests/test_app.py` — +5 token-guard cases +- `job-tracker-ui/src/workflow-trust-signals.test.tsx` — fixture updated + +**Docs** +- `docs/architecture/current.md`, `docs/research/competitors.md`, `docs/decisions/ADR-002-job-application-model.md`, `docs/implementation-roadmap.md`, this report. Ten 0-byte files deleted. + +> **Not mine:** `views/CareerWorkspacePage.tsx` and `views/ProfilePage.tsx` show as modified — that is **your** pre-existing uncommitted work (the inert CV Builder tab). I did not touch either. + +--- + +## 3. Database changes + +Migration `20260717071417_AddJobEntityAndProspectStages`: + +- `JobApplications.DateApplied` → **nullable** (SQLite table rebuild) +- `JobApplications.JobId` → nullable FK to `Jobs`, `SetNull` +- `JobApplications.SavedAt` → new, backfilled `= DateApplied` +- `Jobs` table created (+ `IX_Jobs_OwnerUserId`, `IX_Jobs_CompanyId`, `IX_JobApplications_JobId`) + +### The migration was hand-edited, and that was necessary + +`dotnet ef migrations add` **also scaffolded `CreateTable` for `TrustedDevices`, `TwoFactorRecoveryCodes`, `UserSessions` and `AddColumn` for six `AspNetUsers` columns.** Those tables already exist in every real database — the reconciler in `StartupInitializationExtensions` provisioned them, not a migration, so the prior `ModelSnapshot` never knew them and the scaffolder diffed them as missing. + +**Verified against the live dev database: all three tables are present.** Shipping the scaffolded migration would have failed the deploy with "table already exists". They were removed; the reconciler still creates them on a fresh boot via `CREATE TABLE IF NOT EXISTS`. `IX_JobApplications_OwnerUserId_IsDeleted_Status` was removed for the same reason — the reconciler applies it with the `Status(50)` prefix length MySQL requires and the scaffolded DDL lacks. + +**The regenerated snapshot now includes those tables, so future migrations will no longer re-scaffold them.** This migration closes that drift permanently. + +### Verified against real data, not reasoned about + +EF warned: *"An operation of type 'SqlOperation' will be attempted while a rebuild of table 'JobApplications' is pending."* Rather than trust the reasoning, the migration SQL was applied to a copy of the real dev database (13 rows): + +| Check | Result | +|---|---| +| Rows preserved | 13 → 13 ✅ | +| All pre-existing columns survive the rebuild | 36 → 38 (+`JobId`, +`SavedAt`), **0 dropped** ✅ | +| `DateApplied` values preserved | ✅ | +| `SavedAt` backfilled from `DateApplied` | ✅, no `0001-01-01` sentinels left | +| `DateApplied` nullable afterwards | ✅ | +| `Jobs` created | ✅ | +| Reconciler-era data (`ShortSummary`) preserved | ✅ | + +The only `DROP TABLE` is EF's standard rebuild of `JobApplications` itself (create temp → copy → drop → rename). + +--- + +## 4. Security improvements + +| Finding | Before | After | +|---|---|---| +| **AI sidecar unauthenticated** (High) | Port 8001 published to host; zero auth on `/cv/rewrite`, `/summarize`, `/extract-text`; anyone reaching the host could drain the Gemini/Groq key | **Backend-only, verified live (2026-07-17).** No host port; on a private `ai_internal` network with the backend as its only other member; `X-Ai-Service-Token` required on all non-`/health` endpoints, constant-time compared; compose refuses to start without a token. See §4a. | +| **Auto-ghosting a job nobody applied to** (new risk introduced by Priority 4) | — | Guarded before any date arithmetic; a null `DateApplied` fails safe rather than reading as "infinitely old". Covered by `RulesEngineProspectTests`, including a Saved-a-year-ago job against 1–2 day thresholds | +| **Tenant isolation on the new entity** | — | `Job` carries the same deny-on-null global query filter as every other owned entity | + +### 4a. AI service lockdown — completed and verified 2026-07-17 + +The original Phase 0 fix was **half a fix, and it was never applied**. Two gaps: + +1. **`expose:` only removes the *host* mapping.** `ai-service` was still attached to `shared_services` — which is `external: true` (`jobtracker_shared`). Any other compose stack on the host can join that network and would have reached port 8001. It was also on `default`, which the frontend shares. +2. **Nothing had been deployed.** The running container still mapped `0.0.0.0:8001` because the compose change had never been applied, and `.env` had no `AI_SERVICE_TOKEN` — so the stack would in fact have **refused to start**. + +**Now three layers:** + +| Layer | Mechanism | +|---|---| +| No host port | `ports:` removed, `expose: "8001"` only | +| Private network | New `ai_internal` bridge with exactly two members — `ai-service` and `backend`. Removed from `default` and `shared_services`. Not `internal: true`, so egress to Gemini/Groq still works | +| Shared secret | `X-Ai-Service-Token` on every endpoint but `/health`, `hmac.compare_digest`. `${AI_SERVICE_TOKEN:?...}` on **both** services so a deploy that forgets it fails loudly | + +**Verified against the live stack — not just configured:** + +| Check | Result | +|---|---| +| Host → `localhost:8001/health` | **connection refused** ✅ | +| Frontend container → `ai-service:8001` | **`wget: bad address`** — cannot even resolve it ✅ | +| `/summarize` no token (from `ai_internal`) | **401** ✅ | +| `/cv/rewrite` no token | **401** ✅ | +| `/extract-text` no token | **401** ✅ | +| Wrong token | **401** ✅ | +| `/health` no token | **200** ✅ (the probe and compose healthcheck need it; no data, no generation) | +| **Backend (172.23.0.3) with token → `/summarize`** | **200 OK** ✅ | +| `docker compose config` with empty token | **rejected** ✅ | +| App up | frontend 200, backend 200 ✅ | + +**`AI_SERVICE_TOKEN` was generated and written to `.env`** (64-hex, gitignored, verified). Rotating it requires recreating both containers together — they must agree. + +Bonus verification: recreating the backend applied migration `20260717071417_AddJobEntityAndProspectStages` to the **container's** database cleanly (`__EFMigrationsHistory` row written). That is a second, independent confirmation on a real database, alongside the dev-file test in §3. + +**Caveat for shared Ollama:** if `OLLAMA_BASE_URL` points at an Ollama in another compose stack, address it by host IP — `ai-service` can no longer resolve container names on `shared_services`, by design. The bundled `ollama` profile is on `ai_internal` and still resolves by name. + +### Still open — both need you + +1. **DataProtection keys remain recoverable from git history** (`519c32e`, `955cae6`). Untracked, but not unrecoverable. Flagged 2026-07-03, still open. **Needs an operator with production access — I cannot rotate these.** +2. **CORS wildcard landmine — found during this phase.** `Program.cs:96-102`: if `Cors:Origins` contains `*`, the policy uses `SetIsOriginAllowed(_ => true)` **together with** `AllowCredentials()`. That is reflected-origin-with-cookies: any website could make authenticated requests as the signed-in user. **Not currently active** — compose never sets `Cors__Origins`, so it defaults to `localhost:3000` — but it is one config value from session theft. The fix (reject `*`+credentials) is ~3 lines. **Left alone deliberately: it was outside the five priorities and is not currently exploitable.** Now roadmap task 1.5. + +--- + +## 5. Verification + +| Suite | Result | +|---|---| +| Backend (`dotnet test`) | **232 passed, 0 failed** (was 206 — +26 new) | +| AI sidecar (`pytest`) | **16 passed** (was 11 — +5 new) | +| Frontend typecheck (`tsc --noEmit`) | **clean** | +| Frontend (`react-scripts test`) | 60 passed, **5 failed** — see below | +| Solution build | clean | +| Migration against real data | verified (§3) | +| `docker compose config` without token | correctly rejected | + +### The 5 frontend failures are pre-existing, not mine + +`profile-page.test.tsx` and `settings-view.test.tsx` fail. I verified this rather than assumed it: I stashed **all** Phase 0 changes, re-ran both suites against untouched `main`, and got the **identical 5 failures**, then restored the work and re-confirmed 232 backend tests still pass. + +Neither suite touches anything I changed. Both exercise `ProfilePage`/`SettingsView` — and `ProfilePage.tsx` is part of *your* uncommitted working-tree changes. + +**This means `main` is currently red.** CI runs the whole frontend suite, so every future change lands on a broken baseline. Now roadmap task 1.8. + +### Not verified + +The app was not run end-to-end. Phase 0 changed no UI behaviour by design (the prospect stages are not yet surfaced — that is Phase 1), so there was nothing new to drive in a browser. The migration is verified against real data; a production run is not. + +--- + +## 6. Remaining decisions + +Everything blocking is now either a credential or a small engineering call. + +### Needs you + +1. **Stripe keys** — gates roadmap 7.5. Tiers are decided; only credentials are missing. +2. **DataProtection key rotation** — needs production access. + +### Raised by Phase 0 — needs a call before the phase it touches + +3. **Orphan database tables.** The dev database contains `CareerProfiles`, `CareerProfileVersions`, `CvVariants`, `CvVersions`, `TailoredApplications`, `InterviewPrepNotes`, `AiWorkspaceNotes` — **in no EF model and no migration**. This is an abandoned Career Workspace attempt, and its table names map almost exactly onto roadmap Phase 3 (`CvVariants` is the glossary's "CV Variant"; `CareerProfileVersions` is section history). **Was there a previous design worth recovering, or is this dead weight?** Answer before Phase 3 re-treads the same ground. (Roadmap 1.9.) + +4. **Kanban column count.** The pipeline now has 9 stages; the board is built for 6. Do the three Prospect stages get their own columns, collapse into one "Not applied" column, or sit behind a toggle? Needed to finish Phase 1. (Roadmap 1.1.) + +5. **Dev DB is missing 2 migrations** (`AddCorrespondenceEmailFields`, `AddShortSummary`) yet has `SyncModelSnapshot` — the reconciler patched those columns instead. Local-only, but it means the dev database is not a faithful rehearsal for a production migration. Worth reconciling before the Phase 1 cutover. (Roadmap 1.10.) + +### Deliberately deferred + +- **Backward transition clears `DateApplied`.** Moving `Applied` → `Saved` clears the applied date. The alternative — a Saved job still carrying one — silently counts it as applied in analytics and exposes it to the ghosting rules. The original date stays recoverable from `StatusChanged` history. Documented in ADR-002; flag it if you disagree. +- **`Job` ships empty.** No backfill, by choice: nothing consumes it yet, and a later cutover may shape the data differently. +- **The inert CV Builder tab** (`careerView` prop, never read) is untouched — it is your uncommitted WIP. Roadmap 1.6. diff --git a/docs/product/business-model.md b/docs/product/business-model.md new file mode 100644 index 0000000..fd8932f --- /dev/null +++ b/docs/product/business-model.md @@ -0,0 +1,3 @@ +# business-model + +TODO: Complete documentation. diff --git a/docs/product/goals.md b/docs/product/goals.md new file mode 100644 index 0000000..2babaf0 --- /dev/null +++ b/docs/product/goals.md @@ -0,0 +1,111 @@ +# Product Goals + +## Purpose + +Defines the outcomes Jobjakt should achieve. + +--- + +# Primary Goals + +## Goal 1: Excellent Job Tracking + +Priority: Highest + +Users should have the best possible experience managing applications. + +Requirements: + +- Easy job creation. +- Clear application stages. +- Communication tracking. +- Follow-up reminders. +- Search and filtering. + +--- + +## Goal 2: Build a Reliable Career Profile + +Users should maintain one source of truth. + +Includes: + +- Experience. +- Skills. +- Education. +- Projects. +- Certifications. + +--- + +## Goal 3: Improve Application Quality + +Help users create stronger applications. + +Features: + +- CV generation. +- AI improvements. +- Cover letters. +- Job matching. + +--- + +## Goal 4: Reduce Application Effort + +The system should make repetitive tasks easier. + +Examples: + +- Import jobs. +- Extract CV data. +- Generate documents. +- Reuse career information. + +--- + +## Goal 5: Create SaaS Foundation + +Prepare for future commercialisation. + +Support: + +- Multiple users. +- Billing. +- Premium features. +- Usage limits. + +Do not compromise current UX. + +--- + +# Secondary Goals + +- Job discovery. +- Public CVs. +- Portfolio pages. +- Interview preparation. +- Career analytics. + +--- + +# Success Metrics + +Potential metrics: + +## Engagement + +- Applications tracked. +- Jobs added. +- CVs generated. + +## Quality + +- Completed applications. +- Follow-ups completed. +- User feedback. + +## Retention + +- Returning users. +- Active job searches. \ No newline at end of file diff --git a/docs/product/mission.md b/docs/product/mission.md new file mode 100644 index 0000000..b14bd31 --- /dev/null +++ b/docs/product/mission.md @@ -0,0 +1,76 @@ +# Jobjakt Mission + +## Mission Statement + +Help people organise, improve, and succeed in their careers by combining job tracking, career management, and intelligent AI assistance into one simple workspace. + +--- + +# User Problem + +Modern job searching is fragmented. + +People currently manage: + +- Job links. +- Applications. +- CV versions. +- Cover letters. +- Recruiter conversations. +- Interview preparation. + +Across: + +- Spreadsheets. +- Email. +- Cloud storage. +- Notes applications. +- Multiple AI tools. + +This creates friction and lost opportunities. + +--- + +# Our Solution + +Jobjakt provides one connected workspace where users can: + +- Track opportunities. +- Maintain career information. +- Generate professional documents. +- Receive AI assistance. +- Manage their complete application journey. + +--- + +# Mission Principles + +## Make job searching organised + +Users should always know: + +- What jobs they applied for. +- What stage they are at. +- What happens next. + +--- + +## Make applications better + +Users should be able to create: + +- Better CVs. +- Better cover letters. +- More targeted applications. + +--- + +## Reduce repetitive work + +The system should prevent users from repeatedly entering the same information. + +--- + +## Keep humans in control + +AI should accelerate decisions, not make them for users. \ No newline at end of file diff --git a/docs/product/non-goals.md b/docs/product/non-goals.md new file mode 100644 index 0000000..45c94c0 --- /dev/null +++ b/docs/product/non-goals.md @@ -0,0 +1,66 @@ +# Product Non Goals + +## Purpose + +Defines what Jobjakt should avoid becoming. + +--- + +# Not A Job Board + +Jobjakt should not compete directly with: + +- LinkedIn Jobs. +- Indeed. +- Finn. +- Other job marketplaces. + +Job discovery is an enhancement. + +The main value is managing opportunities. + +--- + +# Not Only A CV Builder + +CV functionality is important but secondary. + +The product is not: + +"Create a CV" + +The product is: + +"Manage your career and applications." + +--- + +# Not A Recruiter Platform + +Recruiter tools are not currently a priority. + +Future consideration only. + +--- + +# Not A Generic AI Chatbot + +AI features should be embedded into workflows. + +Avoid: + +- Empty chat experiences. +- AI without context. +- Generic assistants. + +--- + +# Not Feature Overload + +Do not add features simply because competitors have them. + +Every feature should improve: + +- Finding jobs. +- Applying. +- Managing careers. \ No newline at end of file diff --git a/docs/product/personas.md b/docs/product/personas.md new file mode 100644 index 0000000..0cfe4ad --- /dev/null +++ b/docs/product/personas.md @@ -0,0 +1,121 @@ +# User Personas + +--- + +# Persona 1: Active Job Seeker + +## Description + +Someone actively searching and applying for roles. + +--- + +## Problems + +- Losing track of applications. +- Forgetting follow-ups. +- Managing multiple CV versions. +- Repeating information. + +--- + +## Needs + +- Simple tracking. +- Clear progress. +- Better applications. +- Organisation. + +--- + +## Key Workflow + +Find job + +↓ + +Add application + +↓ + +Prepare documents + +↓ + +Apply + +↓ + +Track progress + +--- + +# Persona 2: Career Changer + +## Description + +Someone moving into a new industry. + +--- + +## Problems + +- Translating experience. +- Knowing transferable skills. +- Creating relevant CVs. + +--- + +## Needs + +- Career profile. +- AI suggestions. +- Tailored documents. + +--- + +# Persona 3: Graduate / Early Career + +## Description + +Someone entering employment. + +--- + +## Problems + +- Limited experience. +- Understanding expectations. +- Building professional documents. + +--- + +## Needs + +- Guidance. +- Templates. +- Structured workflows. + +--- + +# Persona 4: Technical Professional + +## Description + +Experienced worker applying for specialised roles. + +--- + +## Problems + +- Many applications. +- Complex experience. +- Multiple CV variants. + +--- + +## Needs + +- Powerful customisation. +- Automation. +- Detailed tracking. \ No newline at end of file diff --git a/docs/product/roadmap.md b/docs/product/roadmap.md new file mode 100644 index 0000000..03522fa --- /dev/null +++ b/docs/product/roadmap.md @@ -0,0 +1,329 @@ +# Jobjakt Product Roadmap + +## Purpose + +Defines the strategic direction of Jobjakt. + +This roadmap represents product priorities, not a strict development schedule. + +Features should be evaluated against: + +1. Does this improve job tracking? +2. Does this improve application success? +3. Does this improve career management? +4. Does this support future growth? + +--- + +# Product Strategy + +The product evolves in layers. + +The order is: + +Job Tracking Foundation + +↓ + +Application Workflow + +↓ + +Career Workspace + +↓ + +AI Assistance + +↓ + +Automation + +↓ + +SaaS Platform + +--- + +# Phase 1 - Core Job Tracking Excellence + +Priority: Critical + +Goal: + +Make Jobjakt the best place to manage a job search. + +## Features + +### Job Management + +- Add jobs manually. +- Import jobs from URLs. +- Extract job details. +- Store companies. +- Search jobs. +- Filter jobs. + +--- + +### Application Workflow + +Support complete application lifecycle: + +- Interested. +- Preparing. +- Applied. +- Interview. +- Offer. +- Rejected. + +--- + +### Communication Tracking + +Support: + +- Emails. +- Notes. +- Follow-ups. +- Timeline. + +--- + +### Dashboard Improvements + +Provide: + +- Application overview. +- Upcoming actions. +- Progress metrics. +- Recent activity. + +--- + +# Phase 2 - User Experience Improvements + +Priority: High + +Goal: + +Make the application simple and intuitive. + +## Improvements + +- Redesign navigation. +- Improve onboarding. +- Simplify settings. +- Improve empty states. +- Improve error handling. +- Improve mobile experience. + +--- + +# Phase 3 - Career Workspace Foundation + +Priority: High + +Goal: + +Create a single professional source of truth. + +## Features + +Career Profile: + +- Personal details. +- Experience. +- Education. +- Skills. +- Projects. +- Certifications. +- Languages. + +--- + +# Phase 4 - CV Builder + +Priority: High + +Goal: + +Create a premium CV creation experience. + +Inspired by: + +- FlowCV. +- Reactive Resume. +- Novoresume. +- ElegantCV. + +--- + +## Features + +### Content + +Users manage: + +- Personal details. +- Summary. +- Experience. +- Education. +- Skills. +- Projects. +- Certificates. +- Publications. +- Custom sections. + +--- + +### Themes + +Support: + +- Multiple templates. +- ATS-friendly designs. +- Professional layouts. +- Accent colours. +- Typography. +- Photo settings. + +--- + +### Builder + +Support: + +- Live preview. +- Structured editing. +- Section management. +- AI assistance. +- Export. + +--- + +# Phase 5 - AI Career Assistant + +Priority: Medium + +Goal: + +Use AI to reduce repetitive career tasks. + +--- + +Features: + +- CV improvements. +- Bullet rewriting. +- Job matching. +- Cover letters. +- Interview preparation. +- Career advice. + +--- + +AI must: + +- Preserve facts. +- Require user approval. +- Explain changes. + +--- + +# Phase 6 - Job Discovery + +Priority: Medium + +Goal: + +Help users discover opportunities. + +--- + +Potential features: + +- Job search integrations. +- Filters. +- Location search. +- Industry search. +- Remote options. +- Import directly into tracker. + +--- + +Important: + +Job discovery supports tracking. + +It does not replace job boards. + +--- + +# Phase 7 - Public Career Profile + +Priority: Medium + +Goal: + +Allow users to share professional identity. + +--- + +Features: + +- Public CV. +- Portfolio. +- Career profile. +- Shareable URL. + +Example: + +/cv/{random-guid} + +--- + +# Phase 8 - SaaS Platform + +Priority: Later + +Goal: + +Commercialise the platform. + +--- + +Features: + +- User subscriptions. +- Premium themes. +- AI quotas. +- Storage limits. +- Billing. +- Admin management. + +--- + +# Long-Term Ideas + +Possible future features: + +- Interview preparation. +- Salary insights. +- Career analytics. +- Skill recommendations. +- Learning paths. +- Recruiter visibility. +- Portfolio hosting. + +--- + +# Roadmap Rules + +Do not: + +- Build future features before core workflows are excellent. +- Sacrifice simplicity for complexity. +- Add features because competitors have them. + +Always prioritise user value. \ No newline at end of file diff --git a/docs/product/vision.md b/docs/product/vision.md new file mode 100644 index 0000000..4d09884 --- /dev/null +++ b/docs/product/vision.md @@ -0,0 +1,149 @@ +# Jobjakt Product Vision + +## Overview + +Jobjakt is a career management workspace designed to help people successfully navigate the modern job search. + +The application combines job tracking, career management, AI assistance, and document generation into one connected experience. + +The primary goal is to help users move from: + +Finding opportunities + +↓ + +Applying effectively + +↓ + +Managing applications + +↓ + +Improving their chances + +↓ + +Securing employment + +--- + +# Long-Term Vision + +Jobjakt should become the user's personal career operating system. + +Instead of storing job applications in spreadsheets, CVs in random folders, and career information across multiple platforms, users maintain one central workspace. + +The application understands: + +- Their career history. +- Their skills. +- Their experience. +- Their goals. +- Their applications. + +It then helps transform this information into useful career outputs. + +--- + +# Product Positioning + +Jobjakt is not: + +- A simple job board. +- A basic Kanban board. +- Only a CV generator. +- Only an AI writing assistant. + +Jobjakt is: + +A career workspace that helps users manage the entire job application lifecycle. + +--- + +# Core Product Pillars + +## 1. Job Tracking + +The foundation of the product. + +Users should be able to: + +- Store opportunities. +- Track applications. +- Manage stages. +- Record communication. +- Schedule follow-ups. +- Understand their job search progress. + +--- + +## 2. Career Workspace + +A supporting system built around one professional identity. + +Users maintain: + +- Career profile. +- Skills. +- Experience. +- Education. +- Projects. +- Achievements. + +This becomes the source for career outputs. + +--- + +## 3. AI Assistance + +AI should reduce effort. + +Examples: + +- Improve CV wording. +- Analyse job descriptions. +- Suggest improvements. +- Generate cover letters. +- Prepare interviews. + +AI should assist, not replace user control. + +--- + +## 4. Document Generation + +Users should easily create: + +- CVs. +- Tailored CVs. +- Cover letters. +- Public profiles. + +--- + +# Product Evolution + +## Current + +Personal job tracking application. + +## Near Future + +Advanced career workspace. + +## Long Term + +SaaS career platform. + +--- + +# Success Definition + +Jobjakt succeeds when users: + +- Apply to more suitable jobs. +- Spend less time managing applications. +- Produce higher-quality applications. +- Feel organised during job searching. +- Improve career outcomes. diff --git a/docs/research/competitors.md b/docs/research/competitors.md new file mode 100644 index 0000000..ff7d3c5 --- /dev/null +++ b/docs/research/competitors.md @@ -0,0 +1,134 @@ +# Competitor & Market Research — Job Application Tracking (2026) + +> Restored to active docs 2026-07-17 (Phase 0) from `docs/_archive/PRODUCT_RESEARCH.md`. +> Original research conducted **2026-07-02** via the web sources linked throughout. Sources not re-fetched on restore. +> +> **Freshness:** the market analysis (§1, §3) is ~2 weeks old and treated as current. The **feature matrix (§2) was corrected on restore** — several rows were stale because Jobjakt shipped the features. Corrections are marked `[corrected 2026-07-17]` and were verified against code. +> +> **Scope gap:** this covers the **tracker** market (Teal, Huntr, Simplify, Jobscan, OSS). It does **not** cover the CV-builder market — Novoresume, Reactive Resume, and ElegantCV are **not analysed anywhere in this repo**. Reactive Resume is the most relevant (open-source, self-hosted, JSON-Resume-based, data-driven themes — it has already solved the theme problem Phase 4 poses). That research is a prerequisite for CV Builder work; see `docs/implementation-roadmap.md` task 4.9. FlowCV *is* analysed — see `docs/application-discovery-report.md` §7, from local reference downloads at `D:\FlowCV`. + +--- + +## 1. Market landscape + +The market splits into six 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. + +> Note Teal's **free** kanban begins at **Saved** — a pre-application stage. Jobjakt's pipeline began at `Applied` until Phase 0 (2026-07-17) added `Saved`/`Interested`/`Preparing`. + +**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. + +**FlowCV** — CV-builder-first, but **also ships a Job Tracker** — converging on Jobjakt's territory from the CV side. Free tier is one resume ("Your first resume is free forever"); 2+ requires an upgrade. Its `Overview | Content | Customize | AI Tools` structure and fully data-driven theme system are the model for Phase 4; analysed in the discovery report §7. + +**Email auto-trackers** (Trackr, G-Track, extensions) — scan Gmail, AI-classify (Applied/Next step/Rejected/Offer), auto-update statuses, apply labels. 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 Jobjakt: 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). Jobjakt'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, FlowCV's 1 resume). +3. **AI slop** — hallucinated skills, misspelled names, generic bullets; users want AI grounded in *their* real CV. Jobjakt'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 — Jobjakt vs market + +✅ has it · 🟡 partial · ❌ missing · 💰 paid-only + +| Feature | Teal | Huntr | Simplify | OSS self-hosted | **Jobjakt today** | +|---|---|---|---|---|---| +| Kanban pipeline | ✅ | ✅ | ✅ | 🟡 | 🟡 **[corrected 2026-07-17]** `JobPipeline` is canonical + ordered, and Phase 0 added pre-application stages (`Saved`/`Interested`/`Preparing`). Board view exists; **no drag-drop** | +| 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 | ✅ | ✅ | 🟡 | ❌ | 🟡 **[corrected 2026-07-17]** structured CV parse + 6 hardcoded templates + PDF export. **There is no builder** — no content editor, no customisation, no live preview. Discovery report §10 | +| Per-job tailored resume (AI) | 💰 | 💰 | 💰 | ❌ | ✅ **local-AI tailored drafts** (privacy-unique) | +| Resume↔JD match score + keyword gaps | 💰 | 💰 | 🟡 | ❌ | 🟡 **[corrected 2026-07-17]** match score **now exists** (`JobCvMatchService`, `/match-score`, `/candidate-fit`). Keyword-gap analysis still missing | +| 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 + `/analytics` + `/tag-trends`; no funnel/response-rate/time-in-stage | +| Interview management (schedule, prep notes, calendar) | 🟡 | 🟡 | ❌ | ❌ | 🟡 **[corrected 2026-07-17]** `/interview-prep` endpoint exists but **has no UI**; no interview entity, no scheduling | +| Calendar integration (ICS/Google) | 🟡 | 🟡 | ❌ | ❌ | ❌ | +| Salary/offer tracking & comparison | 🟡 | 🟡 | ❌ | ❌ | 🟡 **[corrected 2026-07-17]** salary is **structured** (`SalaryMin`/`Max`/`Currency`/`Period`); no offer-comparison view | +| Autofill applications | ❌ | ✅ | ✅ | ❌ | ❌ (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:** Jobjakt is **ahead of every OSS competitor** and matches or beats paid SaaS on AI drafting, Gmail import, and data ownership. Remaining gaps versus paid SaaS: **keyword-gap analysis**, **a real CV builder**, interview & calendar layer, analytics depth, capture friction (no extension), contact-level CRM. + +--- + +## 3. Market gap — what would make Jobjakt significantly better + +> **"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. Jobjakt is uniquely far along that path. + +--- + +## 4. Ranked feature ideas (value × effort) + +Effort: S (<1 day) · M (1–3 days) · L (1–2 wk) · XL (>2 wk). + +| # | Feature | User impact | Effort | Status 2026-07-17 | +|---|---|---|---|---| +| 1 | **CV↔job match score + keyword gap analysis** | ★★★★★ — the #1 paid feature in the market, free & local here | M–L | **Match score DONE.** Keyword-gap analysis outstanding → roadmap 5.4 | +| 2 | **Canonical pipeline + drag-drop kanban** | ★★★★★ — core daily UX; free-text status blocked analytics | M–L | **Canonical pipeline DONE** + Phase 0 pre-application stages. Drag-drop kanban outstanding | +| 3 | **Analytics dashboard v2** (funnel, response rate, time-in-stage, source effectiveness) | ★★★★ — retention feature; needs #2 | M | Outstanding → roadmap 2.9. Data exists in `JobEvent` | +| 4 | **Interview hub** (rounds, type, scheduled time, prep notes, outcome; ICS + reminders) | ★★★★ — biggest functional gap vs SaaS | L | Endpoint exists, no UI → roadmap 2.7 | +| 5 | **Bookmarklet / minimal browser capture** (reuse `jobimport/preview`) | ★★★★ — kills the biggest friction | S–M | Outstanding → roadmap 6.5. **Product decision 2026-07-17:** discovery order is manual URL import → **browser extension** → official APIs. **Scraping is not the starting point** | +| 6 | **Contacts (people) CRM** | ★★★ | M | Outstanding | +| 7 | **PWA pass** (installable, mobile nav, share-target) | ★★★ | M | Outstanding. Note: CRA is gone — Next.js changes the approach | +| 8 | **Salary/offer tracker** | ★★ | S–M | **Structured salary DONE.** Offer-comparison view outstanding | +| 9 | **Smarter inbox** (AI status suggestions on Gmail review) | ★★★★ — compounds an existing unique strength | M | **`/status-suggestion` DONE** + `EmailStatusClassifier` | +| 10 | **Web push / digest notifications** | ★★ | M | Outstanding | + +**Deliberately not recommended:** auto-apply bots (ToS/ethics/quality; LazyApply-style tools are poorly reviewed, and spray-and-pray contradicts "apply to more *suitable* jobs"); a full Chrome-store extension before a bookmarklet (high maintenance); multi-provider cloud AI (undermines the privacy moat — **confirmed as a product decision 2026-07-17: not building the abstraction**). + +--- + +## 5. Monetisation guidance + +**Product decision 2026-07-17** — Free: job tracking, basic career profile, basic CV. Premium: advanced AI, more themes, automation, analytics, more storage. + +**Do not gate on count** (CV count, job count). That is Huntr's 100-job cap, Teal's AI credits, and FlowCV's 1-resume limit — the exact "free tier caps at the point of seriousness" frustration in §1.4. Jobjakt's moat is privacy + self-hosting + free local AI; a count-based paywall surrenders the moat while inheriting the complaint. + +--- + +## 6. Recommended order (superseded) + +The original 2026-07-02 ordering is superseded by `docs/implementation-roadmap.md`, which sequences against the verified architecture. Items 1, 2, 8 and 9 above are wholly or partly delivered. + +Engineering-health work (god-controller decomposition, backups) is tracked in `docs/architecture/current.md` §17. + +--- + +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) diff --git a/docs/research/cv-builder-patterns.md b/docs/research/cv-builder-patterns.md new file mode 100644 index 0000000..a08422c --- /dev/null +++ b/docs/research/cv-builder-patterns.md @@ -0,0 +1,52 @@ +# CV Builder Patterns + +## Recommended Architecture + +Career Data + +↓ + +CV Builder + +↓ + +Theme + +↓ + +Output + + +--- + +# Good Patterns + +## Structured Content + +User edits: + +- Experience. +- Skills. +- Education. + +Not raw documents. + +--- + +## Live Preview + +Changes immediately visible. + +--- + +## Template Independence + +Content should work with any template. + +--- + +# Avoid + +Canva-style complexity. + +Users should not manually design every pixel. \ No newline at end of file diff --git a/docs/research/elegantcv-analysis.md b/docs/research/elegantcv-analysis.md new file mode 100644 index 0000000..7072a94 --- /dev/null +++ b/docs/research/elegantcv-analysis.md @@ -0,0 +1,45 @@ + +# ElegantCV Analysis + +## Purpose + +Study premium CV aesthetics. + +--- + +# Important Lessons + +Focus on: + +- Visual quality. +- Professional layouts. +- Typography. +- Balance. + +--- + +# Jobjakt Goal + +Generated CVs should feel: + +Premium. + +Modern. + +Professional. + +--- + +# Requirements + +Themes should support: + +- ATS-safe options. +- Creative options. +- Professional options. + +--- + +# Avoid + +Templates that look impressive but fail recruitment systems. \ No newline at end of file diff --git a/docs/research/feature-comparison.md b/docs/research/feature-comparison.md new file mode 100644 index 0000000..9fbc190 --- /dev/null +++ b/docs/research/feature-comparison.md @@ -0,0 +1,32 @@ +# Feature Comparison + +## Purpose + +Compare competitors. + +--- + +| Feature | FlowCV | Novoresume | Reactive Resume | Jobjakt | +|-|-|-|-|-| +| Templates | | | | | +| Live preview | | | | | +| AI writing | | | | | +| Job tailoring | | | | | +| Cover letters | | | | | +| Job tracking | | | | | +| Portfolio | | | | | +| Public CV | | | | | + +--- + +# Goal + +Identify where Jobjakt wins. + +--- + +Expected advantage: + +Competitors create documents. + +Jobjakt manages careers and applications. \ No newline at end of file diff --git a/docs/research/flowcv-analysis.md b/docs/research/flowcv-analysis.md new file mode 100644 index 0000000..b385735 --- /dev/null +++ b/docs/research/flowcv-analysis.md @@ -0,0 +1,89 @@ +# flowcv + +TODO: Complete documentation. +# FlowCV Analysis + +## Purpose + +Analyse FlowCV's CV builder experience. + +Reference: + +https://flowcv.com/ + +--- + +# Key Concepts + +FlowCV provides: + +- Template-based CV creation. +- Structured editing. +- Live preview. +- Design customisation. +- Export functionality. + +--- + +# Important UX Patterns + +## Content / Design Separation + +Users edit content separately from appearance. + +--- + +## Template Switching + +Users can change design without recreating content. + +This aligns with Jobjakt: + +Career Profile + +↓ + +CV + +↓ + +Theme + +--- + +## Builder Structure + +Important concepts: + +Content. + +Design. + +Preview. + +Export. + +--- + +# Features To Consider + +- Section management. +- Theme selection. +- Typography controls. +- Colour controls. +- Layout controls. +- AI assistance. + +--- + +# Jobjakt Adaptation + +Do not copy directly. + +Adapt around: + +Job applications. + +Tailoring. + +Career history. \ No newline at end of file diff --git a/docs/research/job-platform-analysis.md b/docs/research/job-platform-analysis.md new file mode 100644 index 0000000..2959689 --- /dev/null +++ b/docs/research/job-platform-analysis.md @@ -0,0 +1,54 @@ +# Job Platform Analysis + +## Purpose + +Research job discovery opportunities. + +--- + +# Possible Integrations + +Research: + +- Free APIs. +- RSS feeds. +- Public job sources. + +--- + +# Desired Workflow + +Search jobs + +↓ + +Save interesting jobs + +↓ + +Create application + +↓ + +Track progress + +--- + +# Filters + +Potential: + +- Location. +- Job title. +- Industry. +- Salary. +- Remote. +- Skills. + +--- + +# Principle + +Job search enhances tracking. + +It does not replace job boards. \ No newline at end of file diff --git a/docs/research/jobscan.md b/docs/research/jobscan.md new file mode 100644 index 0000000..d7503c1 --- /dev/null +++ b/docs/research/jobscan.md @@ -0,0 +1,3 @@ +# jobscan + +TODO: Complete documentation. diff --git a/docs/research/market-positioning.md b/docs/research/market-positioning.md new file mode 100644 index 0000000..5a6089b --- /dev/null +++ b/docs/research/market-positioning.md @@ -0,0 +1,53 @@ +# Market Positioning + +## Current Market + +Most tools focus on: + +CV creation. + +or + +Job tracking. + +--- + +# Jobjakt Position + +Combine: + +Job tracker + ++ + +Career profile + ++ + +AI assistance + ++ + +Application workflow + +--- + +# Target User + +Someone actively searching for work. + +They need: + +Organisation. + +Better documents. + +Better applications. + +Less stress. + +--- + +# Long Term Vision + +A personal career operating system. \ No newline at end of file diff --git a/docs/research/novoresume-analysis.md b/docs/research/novoresume-analysis.md new file mode 100644 index 0000000..c09db76 --- /dev/null +++ b/docs/research/novoresume-analysis.md @@ -0,0 +1,57 @@ +# novoresume + +TODO: Complete documentation. +# Novoresume Analysis + +## Purpose + +Study professional CV builder patterns. + +--- + +# Strengths + +Known for: + +- Professional templates. +- ATS-friendly layouts. +- Guided writing assistance. + +--- + +# UX Lessons + +Users benefit from: + +- Clear sections. +- Writing suggestions. +- Guided completion. + +--- + +# Opportunities + +Jobjakt can extend this by connecting: + +CV + +↓ + +Job + +↓ + +Application + +↓ + +Follow-up + +--- + +# Features Worth Considering + +- CV scoring. +- Content suggestions. +- Professional templates. +- Guided onboarding. \ No newline at end of file diff --git a/docs/research/overview.md b/docs/research/overview.md new file mode 100644 index 0000000..b831e20 --- /dev/null +++ b/docs/research/overview.md @@ -0,0 +1,74 @@ +# Product Research Overview + +## Purpose + +Document competitor research and product decisions. + +Research should guide: + +- UX decisions. +- Feature priorities. +- Architecture choices. +- Product positioning. + +--- + +# Important Rule + +Competitors should inspire ideas. + +Do not blindly copy. + +Jobjakt has a different purpose: + +A complete job application management platform. + +--- + +# Research Areas + +## CV Builders + +Examples: + +- FlowCV. +- Novoresume. +- Reactive Resume. +- ElegantCV. + +--- + +## Job Platforms + +Examples: + +- LinkedIn. +- Indeed. +- Jobscan. +- Teal. + +--- + +## Productivity Tools + +Examples: + +- Notion. +- Linear. +- Modern SaaS dashboards. + +--- + +# Goal + +Build a product that combines: + +Job tracking. + ++ + +Career management. + ++ + +AI assistance. \ No newline at end of file diff --git a/docs/research/reactive-resume-analysis.md b/docs/research/reactive-resume-analysis.md new file mode 100644 index 0000000..66f492b --- /dev/null +++ b/docs/research/reactive-resume-analysis.md @@ -0,0 +1,50 @@ +# Reactive Resume Analysis + +## Purpose + +Study open-source CV builder architecture. + +--- + +# Strengths + +Important concepts: + +- Open source. +- Custom templates. +- Structured data. +- Modern UI. + +--- + +# Architectural Lessons + +Separate: + +Data model. + +↓ + +Renderer. + +↓ + +Template. + +--- + +# Relevant To Jobjakt + +Supports the idea: + +One career profile. + +Many outputs. + +--- + +# Things To Avoid + +Excessive customisation complexity. + +Users should not need design skills. \ No newline at end of file diff --git a/docs/research/recommendations.md b/docs/research/recommendations.md new file mode 100644 index 0000000..b0ddfa5 --- /dev/null +++ b/docs/research/recommendations.md @@ -0,0 +1,61 @@ +# Product Recommendations + +## Overall Direction + +Jobjakt should become: + +A career workspace that helps people successfully manage job applications. + +--- + +# Core Differentiator + +Not: + +"Create a CV" + +Instead: + +"Manage your entire job search." + +--- + +# Recommended Priority + +## Phase 1 + +Fix fundamentals: + +- Authentication. +- UX. +- Job workflow. + +--- + +## Phase 2 + +Career Workspace: + +- Master profile. +- CV builder. +- Cover letters. + +--- + +## Phase 3 + +AI: + +- Tailoring. +- Suggestions. +- Interview preparation. + +--- + +## Phase 4 + +SaaS: + +- Premium features. +- Billing. +- Public profiles. \ No newline at end of file diff --git a/docs/research/saas-analysis.md b/docs/research/saas-analysis.md new file mode 100644 index 0000000..9019305 --- /dev/null +++ b/docs/research/saas-analysis.md @@ -0,0 +1,36 @@ +# SaaS Research + +## Purpose + +Prepare for future commercialisation. + +--- + +# Current + +Personal-use application. + +--- + +# Future + +Potential SaaS: + +- User accounts. +- Premium themes. +- AI credits. +- Storage limits. + +--- + +# Avoid + +Building billing complexity before product-market fit. + +--- + +# Priority + +Excellent product first. + +Monetisation second. \ No newline at end of file diff --git a/docs/security/ai-security.md b/docs/security/ai-security.md new file mode 100644 index 0000000..980800e --- /dev/null +++ b/docs/security/ai-security.md @@ -0,0 +1,35 @@ +# AI Security + +## Purpose + +Protect against AI-related risks. + +--- + +# Prompt Injection + +Untrusted content: + +- Job descriptions. +- Uploaded CVs. +- Emails. + +must not control AI behaviour. + +--- + +# AI Output Validation + +Check: + +- Unsupported claims. +- Dangerous content. +- Formatting issues. + +--- + +# Data Handling + +Only send required information. + +Avoid unnecessary exposure. \ No newline at end of file diff --git a/docs/security/api-security.md b/docs/security/api-security.md new file mode 100644 index 0000000..7e9f52c --- /dev/null +++ b/docs/security/api-security.md @@ -0,0 +1,40 @@ +# API Security + +## Requirements + +All APIs must: + +- Authenticate correctly. +- Validate input. +- Return safe errors. + +--- + +# Rate Limiting + +Protect: + +- Login. +- AI endpoints. +- File uploads. +- Public CV pages. + +--- + +# API Keys + +Admin-only. + +Never expose: + +- Provider keys. +- Secrets. +- Tokens. + +--- + +# External APIs + +Validate responses. + +Do not trust third-party data. \ No newline at end of file diff --git a/docs/security/authentication.md b/docs/security/authentication.md new file mode 100644 index 0000000..af46b3a --- /dev/null +++ b/docs/security/authentication.md @@ -0,0 +1,84 @@ +# Authentication + +## Purpose + +Define user login and registration. + +--- + +# Supported Methods + +Users should be able to: + +## Email Registration + +Create account using: + +- Email. +- Password. +- Confirmation. + +--- + +## OAuth Registration/Login + +Support: + +- Google. +- Microsoft. + +--- + +# Expected Behaviour + +If OAuth user does not exist: + +Create account automatically. + +Example: + +User clicks: + +"Continue with Google" + +↓ + +Authenticate + +↓ + +No account exists + +↓ + +Create account + +↓ + +Login + +--- + +# Password Requirements + +Support: + +- Secure hashing. +- Password reset. +- Password change. + +--- + +# Authentication UX + +Users should have: + +- Clear errors. +- Loading states. +- Recovery options. + +Avoid: + +"Authentication failed" + +without explanation. \ No newline at end of file diff --git a/docs/security/authorization.md b/docs/security/authorization.md new file mode 100644 index 0000000..6165289 --- /dev/null +++ b/docs/security/authorization.md @@ -0,0 +1,43 @@ +# Authorization + +## Purpose + +Control access to resources. + +--- + +# User Isolation + +A user must only access: + +- Their jobs. +- Their CV. +- Their files. +- Their emails. +- Their settings. + +--- + +# Admin Role + +Admins may access: + +- System settings. +- Provider configuration. +- Monitoring. + +--- + +# API Rules + +Every protected endpoint must verify: + +1. User identity. +2. User permission. +3. Resource ownership. + +--- + +# Never Trust Frontend + +The backend must enforce permissions. \ No newline at end of file diff --git a/docs/security/file-security.md b/docs/security/file-security.md new file mode 100644 index 0000000..743b543 --- /dev/null +++ b/docs/security/file-security.md @@ -0,0 +1,56 @@ +# File Security + +## Purpose + +Protect uploaded documents. + +--- + +# File Types + +Potential uploads: + +- PDF. +- DOCX. +- Images. +- Portfolio files. + +--- + +# Validation + +Check: + +- File type. +- File size. +- File contents. + +Never trust extensions. + +--- + +# Processing + +Uploaded files may contain: + +- Malicious content. +- Embedded scripts. +- AI prompt injection. + +Treat as untrusted. + +--- + +# Storage + +Requirements: + +- Secure naming. +- Access control. +- User ownership checks. + +--- + +# PDF Processing + +Never directly trust extracted text. \ No newline at end of file diff --git a/docs/security/input-validation.md b/docs/security/input-validation.md new file mode 100644 index 0000000..f0b0ca5 --- /dev/null +++ b/docs/security/input-validation.md @@ -0,0 +1,48 @@ +# Input Validation + +## Purpose + +Protect the application. + +--- + +# Validate + +All inputs: + +- Forms. +- APIs. +- Imports. +- Files. +- AI outputs. + +--- + +# Protection Against + +Examples: + +- SQL injection. +- XSS. +- HTML injection. +- Malicious uploads. + +--- + +# CV Builder + +Special attention: + +Users can create: + +- Custom sections. +- HTML-like content. +- Formatting. + +The renderer must sanitise content. + +--- + +# Rule + +Never render user content directly without validation. \ No newline at end of file diff --git a/docs/security/overview.md b/docs/security/overview.md new file mode 100644 index 0000000..a83dbca --- /dev/null +++ b/docs/security/overview.md @@ -0,0 +1,64 @@ +# Security Overview + +## Purpose + +Define security requirements for Jobjakt. + +The application stores sensitive career information including: + +- CVs. +- Employment history. +- Personal details. +- Job applications. +- Documents. +- Communication. + +Security must be considered a core product requirement. + +--- + +# Security Goals + +Jobjakt should provide: + +- Secure authentication. +- Strong user separation. +- Safe document handling. +- Protected APIs. +- Secure AI integrations. + +--- + +# Security Principles + +## Least Privilege + +Users should only access their own data. + +Admins have additional permissions. + +--- + +## Secure By Default + +Features should default to safe behaviour. + +--- + +## Validate Everything + +All external input should be considered untrusted. + +Sources: + +- Forms. +- APIs. +- Uploaded files. +- AI responses. +- Imported job descriptions. + +--- + +## Do Not Over-Engineer + +Security should protect users without harming usability. \ No newline at end of file diff --git a/docs/security/privacy.md b/docs/security/privacy.md new file mode 100644 index 0000000..7c321be --- /dev/null +++ b/docs/security/privacy.md @@ -0,0 +1,51 @@ +# Privacy + +## Principles + +Users own their information. + +--- + +# Requirements + +Support: + +- Data deletion. +- Export. +- Consent. + +--- + +# Public CV + +Users choose: + +Enabled. + +Disabled. + +--- + +# Public URLs + +Use: + +Random UUID. + +Example: + +/cv/8a92f3e2-xxxx + +Avoid: + +Predictable usernames. + +--- + +# Future SaaS + +Consider: + +- Privacy policy. +- GDPR compliance. +- Data processing agreements. \ No newline at end of file diff --git a/docs/security/saas-security.md b/docs/security/saas-security.md new file mode 100644 index 0000000..2d32629 --- /dev/null +++ b/docs/security/saas-security.md @@ -0,0 +1,51 @@ +# SaaS Security + +## Future Requirements + +When moving to SaaS: + +Support: + +- Multiple users. +- Subscription plans. +- Usage limits. + +--- + +# Tenant Isolation + +Every resource belongs to: + +User + +or + +Organisation. + +--- + +# Billing Security + +Never trust client-side: + +- Subscription status. +- Permissions. +- Limits. + +Verify server-side. + +--- + +# Admin Features + +Protect: + +- User management. +- AI providers. +- System settings. + +--- + +# Principle + +Prepare architecture without adding unnecessary complexity early. \ No newline at end of file diff --git a/docs/security/two-factor-authentication.md b/docs/security/two-factor-authentication.md new file mode 100644 index 0000000..c3707f8 --- /dev/null +++ b/docs/security/two-factor-authentication.md @@ -0,0 +1,82 @@ +# Two Factor Authentication + +## Purpose + +Provide additional account protection. + +--- + +# Preferred Method + +TOTP authentication. + +Compatible with: + +- Google Authenticator. +- Microsoft Authenticator. +- Authy. + +--- + +# Flow + +Enable 2FA: + +User requests setup + +↓ + +Generate secret + +↓ + +Display QR code + +↓ + +User scans + +↓ + +User confirms code + +↓ + +Enable 2FA + +--- + +# Login Flow + +Password/OAuth + +↓ + +Request authenticator code + +↓ + +Validate + +↓ + +Login + +--- + +# Recovery + +Support: + +- Backup codes. +- Disable/reset through account recovery. + +--- + +# UX Requirements + +Explain clearly: + +- Why enable 2FA. +- How it works. +- Recovery options. \ No newline at end of file diff --git a/docs/security/uploads.md b/docs/security/uploads.md new file mode 100644 index 0000000..e3bbbc4 --- /dev/null +++ b/docs/security/uploads.md @@ -0,0 +1,3 @@ +# uploads + +TODO: Complete documentation. diff --git a/docs/security/user-data.md b/docs/security/user-data.md new file mode 100644 index 0000000..497a665 --- /dev/null +++ b/docs/security/user-data.md @@ -0,0 +1,48 @@ +# User Data Protection + +## Purpose + +Protect personal career information. + +--- + +# Stored Data + +Potential sensitive information: + +- CV content. +- Employment history. +- Contact information. +- Documents. +- Email communication. + +--- + +# Requirements + +Support: + +- User export. +- User deletion. +- Data ownership. + +--- + +# Database + +Ensure: + +- Correct relationships. +- No cross-user queries. +- Safe migrations. + +--- + +# Logging + +Never log: + +- Passwords. +- Tokens. +- Private documents. +- Sensitive CV content. \ No newline at end of file diff --git a/docs/security/validation.md b/docs/security/validation.md new file mode 100644 index 0000000..0a15e2b --- /dev/null +++ b/docs/security/validation.md @@ -0,0 +1,3 @@ +# validation + +TODO: Complete documentation. diff --git a/docs/technical/coding-standards.md b/docs/technical/coding-standards.md new file mode 100644 index 0000000..ecd8bc9 --- /dev/null +++ b/docs/technical/coding-standards.md @@ -0,0 +1,52 @@ +# Coding Standards + +## General Rules + +Code should be: + +- Readable. +- Self documenting. +- Consistent. + +--- + +# Naming + +Use meaningful names. + +Avoid: + +- Short unclear variables. +- Generic service names. + +--- + +# Methods + +Prefer: + +Small focused methods. + +Avoid: + +Large methods handling multiple responsibilities. + +--- + +# Error Handling + +Errors should: + +- Be handled explicitly. +- Provide useful messages. +- Not expose sensitive information. + +--- + +# Comments + +Comments should explain: + +Why something exists. + +Not what the code does. \ No newline at end of file diff --git a/docs/technical/deployment.md b/docs/technical/deployment.md new file mode 100644 index 0000000..36e95cb --- /dev/null +++ b/docs/technical/deployment.md @@ -0,0 +1,41 @@ +# Deployment + +## Current Goal + +Reliable production deployments. + +--- + +# Deployment Requirements + +Before deploying: + +- Build succeeds. +- Tests pass. +- Database migrations reviewed. + +--- + +# Production Safety + +Avoid: + +- Breaking migrations. +- Downtime. +- Lost data. + +--- + +# Rollback + +Every deployment should have a recovery plan. + +--- + +# Future SaaS + +Consider: + +- Automated deployments. +- Staging environment. +- Deployment approvals. \ No newline at end of file diff --git a/docs/technical/docker.md b/docs/technical/docker.md new file mode 100644 index 0000000..d3f8989 --- /dev/null +++ b/docs/technical/docker.md @@ -0,0 +1,51 @@ +# Docker + +## Purpose + +Maintain consistent environments. + +--- + +# Requirements + +Containers should: + +- Have clear names. +- Use health checks. +- Log correctly. + +--- + +# Images + +Prefer: + +- Official images. +- Pinned versions. + +--- + +# Volumes + +Important data must use persistent storage. + +--- + +# Production + +Monitor: + +- Container health. +- Memory usage. +- Disk usage. +- Restarts. + +--- + +# Common Issues + +Investigate: + +- Container crashes. +- Storage exhaustion. +- Network issues. \ No newline at end of file diff --git a/docs/technical/environment.md b/docs/technical/environment.md new file mode 100644 index 0000000..98d8b45 --- /dev/null +++ b/docs/technical/environment.md @@ -0,0 +1,32 @@ +# Environment Configuration + +## Principles + +Never hardcode secrets. + +--- + +# Configuration + +Use: + +- Environment variables. +- Secret storage. + +--- + +# Examples + +Database credentials. + +API keys. + +OAuth secrets. + +AI provider keys. + +--- + +# Development + +Local configuration should not affect production. \ No newline at end of file diff --git a/docs/technical/git-workflow.md b/docs/technical/git-workflow.md new file mode 100644 index 0000000..a59026a --- /dev/null +++ b/docs/technical/git-workflow.md @@ -0,0 +1,53 @@ +# Git Workflow + +## Branches + +Use feature branches. + +Examples: + +feature/cv-builder-themes + +feature/google-auth + +fix/pdf-parser + +--- + +# Commits + +Commits should: + +- Describe the change. +- Be focused. +- Be reviewable. + +--- + +# Commit Style + +Examples: + +feat: add CV theme selector + +fix: correct PDF extraction + +refactor: extract AI service + +docs: update architecture + +--- + +# Before Commit + +Check: + +- Tests pass. +- Build succeeds. +- No secrets included. + +--- + +# Deployment + +Do not deploy untested breaking changes. \ No newline at end of file diff --git a/docs/technical/logging.md b/docs/technical/logging.md new file mode 100644 index 0000000..fab5f69 --- /dev/null +++ b/docs/technical/logging.md @@ -0,0 +1,45 @@ +# Logging + +## Purpose + +Make debugging possible. + +--- + +# Log Levels + +Use: + +Trace. + +Debug. + +Information. + +Warning. + +Error. + +Critical. + +--- + +# Do Not Log + +Never log: + +- Passwords. +- Tokens. +- API keys. +- Private documents. + +--- + +# Useful Logs + +Include: + +- Operation. +- User context where safe. +- Error details. +- Correlation IDs. \ No newline at end of file diff --git a/docs/technical/monitoring.md b/docs/technical/monitoring.md new file mode 100644 index 0000000..d27115f --- /dev/null +++ b/docs/technical/monitoring.md @@ -0,0 +1,39 @@ +# Monitoring + +## Purpose + +Detect problems early. + +--- + +# Monitor + +Application: + +- Errors. +- Response times. +- Background jobs. + +Infrastructure: + +- CPU. +- Memory. +- Disk. +- Containers. + +--- + +# Alerts + +Important alerts: + +- Application unavailable. +- Database errors. +- Failed deployments. +- Storage issues. + +--- + +# User Experience + +Errors should be visible internally before users report them. \ No newline at end of file diff --git a/docs/technical/overview.md b/docs/technical/overview.md new file mode 100644 index 0000000..8af83c5 --- /dev/null +++ b/docs/technical/overview.md @@ -0,0 +1,41 @@ +# Technical Overview + +## Purpose + +Define engineering standards for Jobjakt development. + +These guidelines help maintain: + +- Code quality. +- Reliability. +- Security. +- Maintainability. + +--- + +# Development Philosophy + +Prefer: + +- Simple solutions. +- Clear code. +- Incremental improvements. +- Tested changes. + +Avoid: + +- Premature optimisation. +- Large rewrites. +- Unnecessary dependencies. + +--- + +# Engineering Priorities + +Order of importance: + +1. User experience. +2. Reliability. +3. Security. +4. Maintainability. +5. Performance. \ No newline at end of file diff --git a/docs/technical/performance.md b/docs/technical/performance.md new file mode 100644 index 0000000..b237e99 --- /dev/null +++ b/docs/technical/performance.md @@ -0,0 +1,36 @@ +# Performance + +## Principles + +Optimise based on evidence. + +--- + +# Important Areas + +Database: + +- Query efficiency. +- Indexes. + +Frontend: + +- Bundle size. +- Rendering. + +Backend: + +- Expensive operations. + +AI: + +- Token usage. +- Response time. + +--- + +# Avoid + +Optimising prematurely. + +Complex caching without measurements. \ No newline at end of file diff --git a/docs/technical/testing.md b/docs/technical/testing.md new file mode 100644 index 0000000..7b6c6eb --- /dev/null +++ b/docs/technical/testing.md @@ -0,0 +1,65 @@ +# Testing Strategy + +## Goal + +Maintain confidence while changing the application. + +--- + +# Test Areas + +## Backend + +Test: + +- Business logic. +- Services. +- APIs. +- Database behaviour. + +--- + +## Frontend + +Test: + +- Important workflows. +- Components. +- User interactions. + +--- + +# Priority Workflows + +Must protect: + +## Authentication + +- Signup. +- Login. +- OAuth. +- Password reset. +- 2FA. + +--- + +## Jobs + +- Creating jobs. +- Updating status. +- Follow-ups. + +--- + +## Career + +- CV upload. +- CV extraction. +- CV generation. +- PDF generation. + +--- + +# Production Confidence + +Critical user journeys should have automated tests. \ No newline at end of file diff --git a/docs/technical/troubleshooting.md b/docs/technical/troubleshooting.md new file mode 100644 index 0000000..d8b476f --- /dev/null +++ b/docs/technical/troubleshooting.md @@ -0,0 +1,32 @@ +# Troubleshooting + +## Purpose + +Provide debugging approach. + +--- + +# General Process + +1. Reproduce issue. +2. Check logs. +3. Identify affected component. +4. Make smallest safe fix. +5. Verify. + +--- + +# Production Issues + +Check: + +- Application logs. +- Docker status. +- Database. +- External services. + +--- + +# Never + +Do not randomly change production configuration without understanding the cause. \ No newline at end of file diff --git a/docs/ux/accessibility.md b/docs/ux/accessibility.md new file mode 100644 index 0000000..9219fe9 --- /dev/null +++ b/docs/ux/accessibility.md @@ -0,0 +1,47 @@ +# Accessibility Requirements + +## Goal + +Make Jobjakt usable by as many people as possible. + +--- + +# Requirements + +Support: + +- Keyboard navigation. +- Screen readers. +- Proper labels. +- Focus states. +- Colour contrast. + +--- + +# Forms + +Every field requires: + +- Label. +- Validation message. +- Helpful error. + +--- + +# Documents + +CV previews should consider: + +- Text readability. +- Contrast. +- Print output. + +--- + +# Testing + +Use: + +- Lighthouse. +- Axe. +- Manual testing. \ No newline at end of file diff --git a/docs/ux/dashboard.md b/docs/ux/dashboard.md new file mode 100644 index 0000000..406fc53 --- /dev/null +++ b/docs/ux/dashboard.md @@ -0,0 +1,73 @@ +# Dashboard Experience + +## Purpose + +The dashboard is the user's command centre. + +It should answer: + +"What should I do next?" + +--- + +# Primary Information + +Display: + +## Application Overview + +Examples: + +- Active applications. +- Interviews. +- Pending follow-ups. +- Recent activity. + +--- + +## Upcoming Actions + +Examples: + +- Follow up with company. +- Prepare interview. +- Complete application. + +--- + +## Progress + +Examples: + +- Applications this month. +- Interview rate. +- Response rate. + +--- + +# Dashboard Actions + +Primary action: + +Add Job + +Secondary: + +- Update CV. +- Review follow-ups. +- Search jobs. + +--- + +# Design Direction + +Inspired by: + +- Notion. +- Linear. + +Avoid: + +- Dense dashboards. +- Too many cards. +- Information overload. \ No newline at end of file diff --git a/docs/ux/design-system.md b/docs/ux/design-system.md new file mode 100644 index 0000000..8eb6600 --- /dev/null +++ b/docs/ux/design-system.md @@ -0,0 +1,74 @@ +# Design System + +## Design Direction + +Jobjakt should feel: + +- Modern. +- Professional. +- Premium. +- Simple. +- Intelligent. + +--- + +# Inspirations + +Notion: + +- Organisation. +- Content editing. + +Linear: + +- Professional software. + +FlowCV: + +- CV building experience. + +--- + +# Colours + +Support: + +- Light mode. +- Dark mode. + +Use: + +- Neutral backgrounds. +- Clear accent colour. +- Strong contrast. + +--- + +# Components + +Maintain consistency: + +- Buttons. +- Forms. +- Cards. +- Dialogs. +- Tables. +- Navigation. + +--- + +# Typography + +Prioritise: + +- Readability. +- Clear hierarchy. +- Professional appearance. + +--- + +# UX Rule + +Every screen should have: + +One obvious primary action. \ No newline at end of file diff --git a/docs/ux/empty-states.md b/docs/ux/empty-states.md new file mode 100644 index 0000000..bddc8d8 --- /dev/null +++ b/docs/ux/empty-states.md @@ -0,0 +1,30 @@ + +# Empty States + +## Purpose + +Empty states should guide users. + +--- + +Every empty state should explain: + +1. What this area does. +2. Why it matters. +3. What action to take. + +--- + +Examples: + +No jobs: + +"Start tracking your job search by adding your first opportunity." + +No CV: + +"Create your career profile to generate your first CV." + +No applications: + +"Import a job to begin tracking your application." \ No newline at end of file diff --git a/docs/ux/errors.md b/docs/ux/errors.md new file mode 100644 index 0000000..221a9a9 --- /dev/null +++ b/docs/ux/errors.md @@ -0,0 +1,29 @@ +# Error Experience + +## Principles + +Errors should be: + +- Clear. +- Helpful. +- Actionable. + +--- + +Avoid technical messages. + +Bad: + +"500 Internal Server Error" + +Better: + +"Something went wrong while importing your CV. Please try again." + +--- + +Include: + +- What happened. +- Possible cause. +- Next action. \ No newline at end of file diff --git a/docs/ux/loading-states.md b/docs/ux/loading-states.md new file mode 100644 index 0000000..d87d8b9 --- /dev/null +++ b/docs/ux/loading-states.md @@ -0,0 +1,29 @@ +# Loading States + +## Requirements + +Loading should always communicate progress. + +--- + +Examples: + +AI generation: + +"Creating your tailored CV..." + +CV extraction: + +"Reading your CV and organising your experience..." + +Job import: + +"Extracting job details..." + +--- + +Avoid: + +Blank screens. + +Unexplained spinners. \ No newline at end of file diff --git a/docs/ux/navigation.md b/docs/ux/navigation.md new file mode 100644 index 0000000..5d66e02 --- /dev/null +++ b/docs/ux/navigation.md @@ -0,0 +1,121 @@ +# Navigation Design + +## Goal + +Navigation should make the application structure obvious. + +--- + +# Primary Navigation + +The application hierarchy: + +## Main + +Dashboard + +↓ + +Jobs + +↓ + +Applications + +↓ + +Calendar / Follow-ups + +--- + +## Career + +Career Profile + +↓ + +CV Builder + +↓ + +Cover Letters + +↓ + +Portfolio + +--- + +## Settings + +Account + +Connected Services + +AI Settings + +Preferences + +--- + +# Sidebar Principles + +The sidebar should: + +- Be predictable. +- Avoid excessive items. +- Group related functionality. + +--- + +# Current Direction + +Move Gmail/email settings into: + +Settings + +↓ + +Connected Accounts + +--- + +# Profile vs Career + +Important distinction: + +## Profile + +User account information: + +- Name. +- Email. +- Password. +- Avatar. +- Preferences. + +--- + +## Career + +Professional identity: + +- Experience. +- Skills. +- CV. +- Documents. + +Do not mix these. + +--- + +# Mobile Navigation + +Mobile should prioritise: + +- Dashboard. +- Jobs. +- Add Job. +- Career. + +Advanced settings can remain secondary. \ No newline at end of file diff --git a/docs/ux/notifications.md b/docs/ux/notifications.md new file mode 100644 index 0000000..8e62cc5 --- /dev/null +++ b/docs/ux/notifications.md @@ -0,0 +1,41 @@ +# Notifications + +## Purpose + +Keep users informed without becoming distracting. + +--- + +Types: + +## Important + +Examples: + +- Authentication issues. +- Failed imports. + +--- + +## Reminder + +Examples: + +- Follow-ups. +- Upcoming interviews. + +--- + +## Informational + +Examples: + +- Completed AI generation. + +--- + +Rules: + +Avoid excessive notifications. + +Users should control preferences. \ No newline at end of file diff --git a/docs/ux/onboarding.md b/docs/ux/onboarding.md new file mode 100644 index 0000000..e03d300 --- /dev/null +++ b/docs/ux/onboarding.md @@ -0,0 +1,181 @@ +# User Onboarding Experience + +## Purpose + +Define how new users should experience Jobjakt from registration to first successful job application workflow. + +The onboarding goal is not to show every feature. + +The goal is to quickly help users achieve value. + +--- + +# Core Principle + +The user should understand: + +"Jobjakt helps me organise my job search and create better applications." + +within the first few minutes. + +--- + +# Current Ideal Flow + +## Step 1 - Registration + +Supported: + +- Email/password registration. +- Google registration. +- Microsoft registration. + +After registration: + +Create user account. + +Verify email where required. + +Redirect to onboarding. + +--- + +# Step 2 - Profile Creation + +Collect basic information: + +Required: + +- Name. +- Email. + +Optional: + +- Profile picture. +- Location. +- Professional title. + +Avoid asking unnecessary questions. + +--- + +# Step 3 - Import Career Information + +Prompt user: + +"Do you already have a CV?" + +Options: + +## Upload CV + +Supported: + +- PDF. +- DOCX. +- Common CV formats. + +System: + +- Extracts information. +- Creates structured career profile. +- Allows user review. + +--- + +## Create Manually + +User builds: + +- Experience. +- Skills. +- Education. + +--- + +# Step 4 - Connect Email + +Optional. + +Purpose: + +Enable: + +- Email tracking. +- Application communication. + +Supported providers: + +- Gmail. +- Microsoft. +- IMAP. + +Do not block the user. + +--- + +# Step 5 - Add First Job + +Guide user through: + +Import job URL. + +↓ + +Extract details. + +↓ + +Review information. + +↓ + +Create application. + +--- + +# Step 6 - Create Application Documents + +Optional workflow: + +CV + +↓ + +Cover Letter + +↓ + +Portfolio + +↓ + +Additional files + +Users can skip any step. + +--- + +# Onboarding Rules + +Never: + +- Force users through long forms. +- Require optional information. +- Hide the skip option. + +Always: + +- Show progress. +- Explain benefits. +- Provide clear next actions. + +--- + +# First Success Moment + +The user should reach: + +"I have my first job application tracked." + +as quickly as possible. \ No newline at end of file diff --git a/docs/ux/profile.md b/docs/ux/profile.md new file mode 100644 index 0000000..e95c51e --- /dev/null +++ b/docs/ux/profile.md @@ -0,0 +1,59 @@ +# Profile Experience + +## Purpose + +Separate personal account information from career information. + +--- + +# Account Profile + +Contains: + +- Profile picture. +- Name. +- Username. +- Email. +- Password. +- Security settings. + +--- + +# Career Profile + +Separate area. + +Contains: + +- Experience. +- Skills. +- Education. +- Projects. +- Certifications. +- Languages. + +--- + +# CV Builder + +The CV builder should not live directly inside profile. + +Recommended: + +Career + +↓ + +Master CV + +↓ + +Builder + +--- + +# Important Rule + +A user's account profile is not their professional profile. + +Keep these separate. \ No newline at end of file diff --git a/docs/ux/responsive.md b/docs/ux/responsive.md new file mode 100644 index 0000000..f3583f5 --- /dev/null +++ b/docs/ux/responsive.md @@ -0,0 +1,47 @@ +# Responsive Design + +## Goal + +Support desktop and mobile users. + +--- + +# Desktop + +Primary experience. + +Optimised for: + +- Job management. +- CV building. +- Document editing. + +--- + +# Tablet + +Support: + +- Dashboard. +- Applications. +- Profile. + +--- + +# Mobile + +Prioritise: + +- Checking applications. +- Adding jobs. +- Follow-ups. + +Complex CV editing may be simplified. + +--- + +# Rule + +Do not simply shrink desktop layouts. + +Design mobile experiences intentionally. \ No newline at end of file diff --git a/docs/ux/settings.md b/docs/ux/settings.md new file mode 100644 index 0000000..1aff936 --- /dev/null +++ b/docs/ux/settings.md @@ -0,0 +1,88 @@ +# Settings Experience + +## Purpose + +Settings should allow users to manage their account and application preferences. + +--- + +# Structure + +Recommended tabs: + +## General + +Contains: + +- Appearance. +- Language. +- Preferences. + +Remove unnecessary complexity. + +--- + +## Account + +Contains: + +- Name. +- Email. +- Password. +- Security. + +--- + +## Connected Accounts + +Contains: + +Email providers: + +- Gmail. +- Microsoft. +- IMAP. + +Future: + +- Other integrations. + +--- + +## AI Settings + +Contains: + +- AI provider selection. +- Usage settings. +- Model preferences. + +Admin controls should remain restricted. + +--- + +## Notifications + +Contains: + +- Email notifications. +- Follow-up reminders. +- Application alerts. + +--- + +# Rules + +Normal users should not see: + +- System management. +- Import/export administration. +- Infrastructure controls. + +--- + +# UX Goal + +Settings should feel like a normal modern application. + +Not an admin panel. \ No newline at end of file diff --git a/docs/ux/user-flows.md b/docs/ux/user-flows.md new file mode 100644 index 0000000..6f41a10 --- /dev/null +++ b/docs/ux/user-flows.md @@ -0,0 +1,99 @@ +# User Flows + +## Add Job Flow + +Recommended: + +Add Job + +↓ + +Enter URL + +↓ + +Import Details + +↓ + +Review Information + +↓ + +Create Application + +↓ + +CV + +↓ + +Cover Letter + +↓ + +Portfolio + +↓ + +Files + +↓ + +Complete + +--- + +# CV Creation Flow + +Career Profile + +↓ + +Generate CV + +↓ + +Choose Theme + +↓ + +Customise + +↓ + +Preview + +↓ + +Export + +--- + +# Tailored Application Flow + +Job + +↓ + +Analyse Requirements + +↓ + +Generate Suggestions + +↓ + +Create Tailored CV + +↓ + +Review + +↓ + +Save Copy + +↓ + +Export \ No newline at end of file