Files
jobtrackingapp/docs/remaster/MIGRATION_PLAN.md
T
cesnimda f0f178d77e docs(remaster): full-system audit + rebuild-vs-refactor decision
Deep, code-grounded audit of Job Tracker producing the mission deliverables
under docs/remaster/: system audit, bug report, architecture/data-model/AI/UX
reviews, remaster proposal, migration plan, competitor research, and the gated
REBUILD_DECISION.

Verdict: Incremental Refactor (no full rebuild). Evidence: no Critical defects;
hardened cookie/CSRF auth (token never in JS storage), real SSRF defence,
enforced multi-tenancy via global query filters, decoupled provider-swappable
AI service, 135 backend tests. Debt is localised (god controllers/entity,
missing hot-path indexes, prompt-injection hardening, CRA build debt) and
reachable by in-place, test-guarded refactors.

Also harden .gitignore: exclude agent tooling (.claude/, .bg-shell/, .agent.md)
and restore/broaden the runtime-secrets block (**/keys/, **/backups/, exports,
CV artifacts) so nested DataProtection keys can't be committed accidentally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:04:03 +02:00

4.6 KiB

Migration / Remaster Plan — Job Tracker

Companion to: REMASTER_PROPOSAL.md · Decision: REBUILD_DECISION.md

Strategy: incremental, test-guarded, feature-branch per unit (matches .gsd D017 slice discipline and the project's no-direct-main / conventional-commit rule). The 135 backend integration tests + 23 frontend suites are the safety net that makes internal change low-risk. No big-bang.

Guardrails per slice

  1. Branch off main; conventional commit; no direct main pushes; no auto-merge.
  2. dotnet build -c Release + dotnet test JobTrackerApi.Tests green before commit.
  3. Frontend: full Jest suite green.
  4. One PR per slice → one CI run on the Pi (single-capacity runner).
  5. Behaviour preserved; add a targeted test if a slice exposes a coverage gap.

Wave 1 — Performance (lowest risk, highest ROI) — this was the paused Phase 7 work

  • P1. Hot-path indexes (Data/JobTrackerContext.cs + one migration): IsDeleted, (IsDeleted,Status), FollowUpAt, Correspondence.JobApplicationId, JobEvent.JobApplicationId. SQLite+MySQL safe.
  • P2. Server-side aggregation for GetStats/GetAnalyticsOverview (no full-table ToListAsync).
  • P3. Gmail N+1 batch fixes (:646, :701, :893) + FirstOrDefaultAsync for review decisions.
  • P4. RuleSettings cache in IMemoryCache (short TTL, per user).
  • AI provider router in app.py (AI_PROVIDER={ollama|gemini|groq}) + /health reports provider; default stays ollama (keyless). Prod .env sets AI_PROVIDER=gemini + rotated key → offloads the 1060.

Wave 2 — Safe refactors (behaviour-preserving)

  • R1. Extract services from JobApplicationsController: AnalyticsService, JobStatsService, CvContextBuilder. Controller shrinks to a thin adapter.
  • R2. Extract GmailImportService + GmailThreadRefresher from GmailController.
  • R3. DTO extraction for JobApplicationsController/ProfileCvController/GmailController.
  • New files under Controllers//Services/ so the JobTrackerBackend glob picks them up; no Program.cs DI churn beyond registering the new services.

Wave 3 — Data-model evolution (additive migrations + backfill)

  • D1. Attachment booleans → computed. Migration + backfill verification test; then drop stored columns.
  • D2. Single tailored-CV source. Migrate TailoredCvTextTailoredCvDraft; deprecate the string.
  • D3. Split JobImportContent 1:1 off JobApplication.
  • D4. CvVersion + CoverLetter first-class tables (enables real versioning promised by the product). Each is a reversible EF migration; run against a SQLite dev DB and a MariaDB staging copy before prod.

Wave 4 — AI hardening + UX

  • A1. Prompt-injection delimiters + input normalisation; factuality diff vs StructuredCvProfile.
  • A2. Match-score synonym map + relabel; matched/missing breakdown in the UI.
  • U1. Import partial-parse state; dedicated application-answer field; AI-fabrication confirm UI.

Wave 5 — Frontend platform (decide first)

Resolve the .gsd "use next.js" override deliberately:

  • Least churn: CRA → Vite (drops most transitive-vuln debt, keeps React/MUI, fast).
  • If public/SEO product: Next.js (honours the override; SSR/routing/metadata) — larger effort. Do this as its own milestone, not coupled to backend work.

Risk assessment

Risk Likelihood Mitigation
Migration data loss (Wave 3) Low additive + backfill + staging dry-run on MariaDB copy + backups (already automated)
Behaviour regression in extraction Low 135 integration tests lock the API contract
Single-runner CI bottleneck Medium one PR per slice; keep slices small
Provider-router auth leak Low key from env only; never logged/committed; rotate the pasted key
Frontend migration churn Medium isolate as its own milestone; feature-flag if needed

Preserve vs discard

  • Preserve unchanged: auth (cookie+CSRF), SSRF ingestion guard, global query filters, deterministic services, AI HTTP boundary, background-service model (single-node), test suites, deploy pipeline.
  • Refactor before reuse: the three god controllers, JobApplication entity, prompt construction.
  • Discard: attachment boolean columns (after backfill), inline TailoredCvText/CoverLetterText strings (after migration), scraper reliance as a guarantee (keep as best-effort).
  • .gsd logic: treat as historical design intent (already mostly realised); resolve the two open items (next.js override, notes-block workaround). The .gsd folder is git-ignored and stays out of the repo.