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>
This commit is contained in:
cesnimda
2026-07-05 10:04:03 +02:00
parent 657cb95a48
commit f0f178d77e
12 changed files with 795 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
# Architecture Review — Job Tracker
**Companion to:** [SYSTEM_AUDIT_REPORT.md](SYSTEM_AUDIT_REPORT.md)
## 1. Current topology (as built, verified)
```
React 19 / TS / MUI 7 (CRA) ──HTTP(cookie+CSRF)──▶ ASP.NET Core API (net9.0, EF Core 9)
job-tracker-ui/ JobTrackerApi/ (+ JobTrackerBackend link-compile)
┌─────────────────────┼───────────────────────┐
▼ ▼ ▼
EF Core / SQLite|MySQL Hosted services HttpClient ──▶ FastAPI AI svc
(global query filters) (reminders, rules, tools/summarizer/
enrichment, export, Ollama | distilbart
backup) (provider-swappable)
──▶ Gmail API (OAuth), LibreTranslate
```
## 2. What is genuinely good (keep)
- **AI service decoupling `[strength]`.** The .NET side (`SummarizerService`, `CvAiClassifier`,
`CvAiNormalizer`) only speaks HTTP to the FastAPI service. Swapping Ollama→Gemini/Groq is a change in
*one* Python file with *zero* .NET edits. This is textbook boundary placement.
- **Multi-tenancy via global query filters** on `OwnerUserId` in `Data/JobTrackerContext.cs`. Centralised,
hard to bypass accidentally, covered by `JobApplicationsAuthorizationTests`.
- **SSRF-safe ingestion** (`JobImport/JobImportService.cs:133-210`): scheme allowlist, loopback/private/
CGNAT/link-local/IPv6-ULA blocklist *after DNS resolution*, redirect-averse fetch, 4 MB cap.
- **Deterministic domain services** — `JobCvMatchService`, `JobPipeline`, `StageAnalytics`,
`EmailStatusClassifier` are small, pure, unit-testable. This is the model the controllers should follow.
- **Provider-agnostic persistence** — SQLite default, Pomelo MySQL/MariaDB for prod.
## 3. Architectural weaknesses
### 3.1 God controllers `[Architectural weakness]` — highest impact
`JobApplicationsController` = **3,271 lines**, `ProfileCvController` = **2,265**, `GmailController` =
**1,179**. These are transaction scripts: they hold orchestration, validation, AI-context assembly,
persistence, and DTO shaping inline. Consequences: untestable in isolation, merge-conflict magnets,
duplicated `RulesEngine.GetSettings` calls, and read paths that load whole tables then filter in memory.
**Fix:** extract cohesive services (`JobStatsService`, `AnalyticsService`, `CvContextBuilder`,
`GmailImportService`, `GmailThreadRefresher`) + DTO files. The 135 integration tests make this safe.
### 3.2 Build-layout footgun `[Architectural weakness]`
Controllers/services compile through a **separate `JobTrackerBackend` library** that globs
`../JobTrackerApi/Controllers/**/*.cs` and `../Services/**/*.cs`, *not* through `JobTrackerApi.csproj`.
New files "just compile" from the right folder — invisible magic that will confuse every new contributor.
**Fix:** document loudly (done in CLAUDE/README) or collapse the split; not urgent.
### 3.3 Polling background services, no event bus `[Design flaw, low severity]`
Reminders/rules/enrichment run on timers. Fine for a single node and a personal/low-tenant load; would
need an outbox/queue if this becomes real multi-tenant SaaS. Not a problem *today*.
### 3.4 Frontend build platform `[Architectural weakness]`
CRA / `react-scripts 5` is EOL-ish and carries transitive-vuln debt (`.gsd` D019 remediated only the
direct `axios` finding and explicitly deferred the framework migration). **And** `.gsd/OVERRIDES.md`
records an **active** directive *"use next.js"* (2026-04-10) that was **never executed**. So the shipped
stack contradicts the last recorded frontend decision. Resolve intentionally (Vite for least churn, or
Next.js per the override if SSR/SEO for a public product matters).
### 3.5 Scraper-plugin fragility `[Architectural weakness]`
HTML-structure-coupled plugins against adversarial targets (LinkedIn/Indeed) will rot. No plugin-health
metric, so failures are silent (fall back to universal parser or manual). **Fix:** health telemetry +
lean on the already-solid manual fallback; treat scraping as best-effort, not a guarantee.
## 4. Service-boundary map (target)
| Concern | Today | Target owner |
|---|---|---|
| Job CRUD | `JobApplicationsController` | thin controller → `JobApplicationService` |
| Stats/analytics | inline in controller (load-all) | `AnalyticsService` (server-side aggregation) |
| CV context assembly | inline in `JobApplicationsController`/`ProfileCvController` | `CvContextBuilder` |
| Gmail import/refresh | `GmailController` (N+1) | `GmailImportService` + `GmailThreadRefresher` |
| Rule settings | repeated `RulesEngine.GetSettings` | cache in `IMemoryCache` (already registered) |
## 5. Verdict
The **skeleton is correct**; the muscle is in the wrong place (controllers). This is the signature of a
system that grew feature-first, not of one that is architecturally unsound. Refactor, do not rebuild.