f0f178d77e
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>
68 lines
4.6 KiB
Markdown
68 lines
4.6 KiB
Markdown
# Migration / Remaster Plan — Job Tracker
|
|
|
|
**Companion to:** [REMASTER_PROPOSAL.md](REMASTER_PROPOSAL.md) · **Decision:** [REBUILD_DECISION.md](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 `TailoredCvText` → `TailoredCvDraft`; 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.
|