Files
jobtrackingapp/docs/implementation-roadmap.md
cesnimda 06518a7d52
CI and Deploy / test (pull_request) Successful in 4m42s
CI and Deploy / deploy (pull_request) Has been skipped
CI and Deploy / test (push) Successful in 4m2s
CI and Deploy / deploy (push) Failing after 1m15s
fix: require verified production email
Expose the existing email-verification setting through Compose and close the completed DataProtection rotation blocker.
2026-08-02 17:59:05 +02:00

264 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Jobjakt — Implementation Roadmap
Date: 2026-07-31 · **Reconciled after the technical-debt pass**
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` 24days · `L` ~12wk · `XL` 2wk+
**Current state:** Phases 07 are repository-complete. Opening registration, activating Stripe, and deploying the release require operator configuration/access. See `BLOCKERS.md`.
---
## 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 ✅ CODE COMPLETE
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 | ✅ **DONE**`Saved`, `Interested`, and `Preparing` are exposed through the shared pipeline model, grouped Kanban, filters, and status menus. | **P0** | **M** | Phase 0 | Prospect workflow is visible end to end. |
| 1.2 | ✅ **DONE (2026-07-30)** — new jobs default to `Saved`; users can choose any later stage in the wizard. | **P0** | **XS** | 1.1 | New opportunities no longer imply an application was already submitted. |
| 1.3 | ✅ **DONE** — draggable cards move between grouped Kanban columns, persist the destination entry stage, and retain precise stage selection in the card menu. | **P2** | **M** | 1.1 | Covered by grouped-board drag/drop regression tests. |
| 1.4 | **Rotate DataProtection keys** | **P1** | **XS** | none | **Complete 2026-08-02.** Production keys rotated by the operator. |
| 1.5 | ✅ **DONE (2026-07-30)** — startup rejects wildcard CORS origins when credentialed requests are enabled. | **P1** | **XS** | none | Unsafe configuration now fails closed. |
| 1.6 | ✅ **DONE** — the dead `careerView` prop/tab is gone; the implemented CV Builder has its own routed workspace. | **P1** | **XS** | none | No dead navigation remains. |
| 1.7 | ✅ **DONE** — onboarding reads structured career-profile collections with raw CV text only as fallback. | **P2** | **XS** | none | Parsed profiles are recognized correctly. |
| 1.8 | ✅ **DONE** — full frontend suite is green (145 tests on 2026-07-31). | **P1** | **S** | none | CI has a clean regression baseline. |
| 1.9 | ✅ **DONE** — the useful Career Workspace design/code was recovered and superseded by the completed Phase 3/4 implementation. The old branch is historical and is not a merge target. | **P0** | **M** | none | No longer blocks later phases. See `docs/career-workspace-branch-assessment.md` for the original assessment. |
| 1.10 | ✅ **DONE (2026-07-30)** — restored EF identity metadata for the two orphan migrations as safe no-op history markers and brought the development database fully current. | **P2** | **S** | none | The idempotent reconciler remains the schema owner for those legacy columns; EF migration history is now complete with no pending migrations. |
**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 ✅ DONE (2026-07-30)
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`~~**DONE (2026-07-17, commit `66cc6a7`)**`/profile` = identity + security + preferences; `/career` = master career profile. The two saves are now scoped (partial-update-safe `PUT /auth/profile`) so neither wipes the other; the inert CV-Builder tab and dead `careerView` prop are gone. **The master profile is the source of truth; generated docs reference snapshots (not built yet).** | **P1** | **M** | none | Fixed the guide's "everything should have one obvious place". Functional separation via the existing `careerOnly` fork + scoped saves. |
| 2.2 | ✅ **DONE (2026-07-30)** — decomposed identity/security and Career Workspace into separate `ProfilePage` and `CareerProfilePage` components, with career sections extracted into focused presentational components. | **P1** | **M** | 2.1 | Phase 2 delivered the *functional* split via the `careerOnly` fork + scoped saves, but it is still one file behind a boolean. Extracting `ProfilePage` (identity/security) and a `CareerWorkspace` content component (master profile) is the remaining cleanup — mechanical, deferred so Phase 2 stayed low-risk. Blocks nothing; do before heavy Phase 3/4 edits. |
| 2.3 | ✅ **DONE (2026-07-30)**`Signup → Verify → Profile → Import CV → Connect email → First job`; the dashboard progress flow reuses existing signup/verification screens, reads structured career data, detects Gmail/Outlook/IMAP connections, and disappears when complete. | **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 | ✅ **DONE (2026-07-30)** — dedicated `/register` route reuses the hardened auth form and clearly disables submission when registration is unavailable. | **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 | ✅ **DONE** — incremental cache equivalents ship through `useViewResource` for shared server state and `useWorkspaceTabCache` for expensive workspace tabs; no dependency added. | **P2** | **M** | none | Root cause of the 6001400-line components and the hand-rolled `refreshToken` prop-threading. Pays for itself across Phases 36. Adopt incrementally, not as a rewrite. |
| 2.6 | ✅ **DONE (2026-07-30)** — extracted shared presentation cards and four intelligence tab panels into `JobDetailsPanels` and `JobInsightTabs`; the dialog remains the orchestration boundary. | **P2** | **M** | 2.5 | A dialog carrying an entire workspace. |
| 2.7 | ✅ **DONE** — user-owned interview preparation is exposed in `ApplicationWorkspacePage`, reachable from workflow signals and reminders, with grouped editable prep items. | **P2** | **S** | none | `/jobapplications/{id}/interview-prep` already works and is invisible. Cheap win — a shipped feature nobody can reach. |
| 2.8 | ✅ **DONE (2026-07-30)** — direct Jest + Babel configuration replaces `react-scripts`; packages were promoted from the existing lockfile without a new download. | **P2** | **M** | none | Removes one of three frontend toolchains. Do not touch the router in the same change. |
| 2.9 | ✅ **DONE** — dashboard analytics include funnel, response rate by source, top companies, and time-in-stage backed by `AnalyticsService`. | **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.
> **Foundation SHIPPED 2026-07-18** (commits `9c8644e`…`a1dd447`). The relational master career
> profile is built, tested (23 backend + frontend tests), migrated (verified on the live DB), wired
> to `/career`, with completeness overview + version-history restore. See
> `docs/architecture/career-profile-model.md` (Implementation status). **Phase 3 completed 2026-07-30:**
> the workspace, Master CV separation, variants, long-tail sections, extraction retention, and
> file-backed avatars are delivered.
| # | Task | Priority | Difficulty | Dependencies | Expected value |
|---|---|---|---|---|---|
| 3.1 | ✅ **DONE** — Experience/Education/Skills/Projects/Certifications/Languages are relational children of `CareerProfile`; long tail is JSON. Projection service keeps the blob as a derived read-model; lazy backfill from existing data. | **P1** | **L** | 1.9 | Delivered. Queryable structured career data is now the source of truth; the blob is derived. |
| 3.2 | ✅ **DONE (2026-07-30)** — added Awards, Publications, Organisations, and References across extraction, review/merge, JSON storage, editing, and rendering. | **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 | ✅ **DONE (2026-07-30)** — dedicated Career Workspace page and focused section components. | **P1** | **M** | 2.1, 2.2 | Currently a wrapper around `ProfilePage`. |
| 3.4 | ✅ **DONE (2026-07-30)** — Career Profile is the source of truth; Master CV is a generated representation. | **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 | ✅ **DONE (2026-07-30)** — named CV variants with stable profile-item references. | **P2** | **M** | 3.4 | In the glossary; no code. Distinct from per-application `TailoredCvDraft`, which works correctly and must not be disturbed. |
| 3.6 | ✅ **DONE (2026-07-30)** — retain the newest 20 completed extraction runs per user; queued/running work is protected. | **P2** | **S** | none | Three copies of every CV (raw/normalized/structured), unbounded. |
| 3.7 | ✅ **DONE (2026-07-30)** — new avatars live in persistent file storage; the DB stores only an internal file reference, with legacy data-URL compatibility. | **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`).
**Completed 2026-07-30.** The builder now covers content, customisation, preview, export, variants, and public sharing.
> **Foundation SHIPPED 2026-07-18** (commits `a3e18e4` backend, `158dd02` frontend). Data-driven
> theme engine + variant model + 3-tab builder + live preview + public CV, all consuming the master
> profile (never duplicating it). See `docs/architecture/cv-builder.md` and
> `docs/architecture/cv-theme-engine.md`. Status: 4.1 ✅ `CvTheme` model. 4.2 ✅ one
> `ThemedCvRenderer` (the old `CvTemplateRenderer` stays only for the legacy tailored-draft flow).
> 4.3 ✅ 8 themes as data. 4.4 ✅ Content tab reorder/hide/rename + custom sections (up/down
> controls with keyboard support; **native drag-and-drop deferred** — no dnd dependency added yet).
> 4.5 ✅ Customise tab (theme, accent, fonts, density, page size, photo/icons/page-numbers). 4.6 ✅
> live preview (server render on a 350 ms debounce for export fidelity). 4.7 ✅ PDF export wired into
> the builder. Plus: autosave + version history/restore, AI-assist (suggestion-only), and public CV at
> `/cv/{slug}`.
>
> **Phase 4.5 — Builder Polish SHIPPED 2026-07-18** (commits `585047d`, `e3b255f`, `582c4e0`).
> Public-CV deep links fixed (optional catch-all `app/[[...slug]]`; direct nav/refresh/shared links
> work, no infra change). Native drag-and-drop reorder for sections + entries (keyboard arrows kept).
> Per-item editing via `GET /api/cv/outline`: hide, title/subtitle override, rich-text bullets
> (`**bold** *italic* __underline__ [link]`, escaped-then-whitelisted server-side). `ItemOrder`
> reorders entries without touching the master profile. Preview: zoom presets, measured page count +
> page navigation + page-break indicators, "updating" state. Unsaved/Saving/Saved indicator, loading
> skeletons, better empty states, ATS-friendly theme badge, a11y (ARIA labels, keyboard theme cards,
> focus rings), print-quality page-break CSS, AA contrast fix. Docs: `cv-builder.md`,
> `cv-theme-engine.md`. Server-rendered preview remains intentional for export fidelity; the controller was
> split into endpoint, pipeline, and parsing partials on 2026-07-30.
| # | Task | Priority | Difficulty | Dependencies | Expected value |
|---|---|---|---|---|---|
| 4.1 | ✅ **DONE** — 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 | ✅ **DONE** — 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 | ✅ **DONE** — Seed 35 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 | ✅ **DONE** — 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 | ✅ **DONE** — Customise tab — the ~12 controls from 4.1 | **P1** | **M** | 4.1, 4.2 | Currently **nothing** is customisable. |
| 4.6 | ✅ **DONE** — Live server-rendered 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 | ✅ **DONE** — 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 | ✅ **DONE (2026-07-30)** — Split `ProfileCvController` (split from 2,379 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.
> **AI Career Assistant SHIPPED 2026-07-18** (commits `f299d7b` backend, `bb0c0fe` frontend). A unified
> per-application **AI Workspace** tab with five suggestion modules — job-analysis, career-match,
> cover-letter (6 tones), interview, application-review — each running through the existing
> `ISummarizerService`/ai-service provider abstraction and stored as **append-only history**
> (`AiInteraction`) with reuse / compare / copy / delete. Suggestion-only throughout; nothing
> auto-applies; `Markdown` renders React nodes (no HTML-injection surface). See
> `docs/architecture/ai-career-assistant.md`. **Phase 5 completed 2026-07-30:** provider-neutral usage
> metering and visible monthly totals now close the Phase 7 cost-control prerequisite. Per-request
> provider choice remains deliberately deferred by ADR-004.
| # | Task | Priority | Difficulty | Dependencies | Expected value |
|---|---|---|---|---|---|
| 5.1 | ✅ **DONE (2026-07-30)** — fixed `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 | ✅ **DONE (2026-07-30)** — 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 | ✅ **DONE (2026-07-30)** — surfaced optional 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 | ✅ **DONE** — 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 | ✅ **DONE (2026-07-30)** — wrote 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. **URL preview and bookmarklet/PWA capture are complete.** 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 | **DONE (2026-07-30)** — manual URL import previews into the existing reviewed add-job flow, which persists through the normal create endpoint and warns on duplicate URLs. No second import-history store was added. | **P2** | **M** | Phase 0 | The existing flow delivers the discovery win without another persistence model. |
| 6.2 | **DONE (2026-07-30)** — bookmarklet/PWA share capture reuses `jobimport/preview` and opens the reviewed add-job flow in `Saved`. A store extension remains deliberately unnecessary. | **P2** | **L** | 6.1, 1.1 | Capture is available without Chrome-store maintenance. |
| 6.3 | **DONE (2026-07-30)** — NAV Job Vacancy Feed integration uses the official authenticated API and rotating public experiment token; `NavJobs:Token` supports a stable private token later. FINN requires a business agreement, while Indeed and LinkedIn do not expose open discovery feeds. | **P3** | **L** | 6.2, 6.6 | Legal official Norway-first discovery without scraping. |
| 6.4 | **DONE (2026-07-30)** — discovery UI searches recent active NAV events by title/company and municipality. | **P3** | **M** | 6.3 | Provides a usable official-feed search surface. |
| 6.5 | **DONE (2026-07-30)** — “Save to tracker” routes discoveries through the existing reviewed URL-import wizard and lands them in `Saved`. | **P3** | **S** | 6.3 | Reuses the established import and duplicate-warning flow. |
| 6.6 | ✅ **DONE (2026-07-30)** — application creation dual-writes the `Job` opportunity; importer results carry provider source and market metadata, with NAV/FINN/Jobbnorge declaring `NO`. | **P2** | **M** | none | Norway-first provider data now drives the write path without making Norway a global default. |
| 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 | **IMPLEMENTED AND CONFIGURED; browser verification required** — password signup and sign-in use Cloudflare Turnstile with mandatory server-side Siteverify validation. Production reports registration and Turnstile enabled; a real signup still needs interactive verification. | **P2** | **M** | 2.4, 7.3 | The code and configuration are active without weakening abuse controls. |
| 7.2 | **DONE (2026-07-30)** — existing Identity roles are the plan model: `Premium` (and `Admin`) receives `advancedAi`, `premiumThemes`, `automation`, `analytics`, and 5 GB storage capabilities; free accounts receive core features and 250 MB. `/auth/me` exposes plan and entitlements. | **P3** | **M** | none | Reuses the existing role system and avoids a second billing-state table before Stripe exists. |
| 7.3 | **DONE (2026-07-30)** — existing AI interaction metering now enforces monthly generation limits: 25 for free accounts and 250 for Premium/Admin. Usage responses expose the active plan and limit. | **P3** | **M** | 5.2, 7.2 | Cost-bearing AI now has a clear monthly ceiling before registration opens. |
| 7.4 | **DONE (2026-07-30)** — attachment uploads enforce total per-user storage entitlements (250 MB free, 5 GB Premium/Admin) in addition to the existing 10 MB per-file cap. | **P3** | **S** | 7.2 | Storage limits match the exposed capability model. |
| 7.5 | **IMPLEMENTED; configuration required (2026-07-31)** — hosted subscription Checkout, customer portal, signed subscription webhooks, persisted Stripe state, and idempotent Premium-role provisioning. | **P3** | **L** | 7.2 | Activation needs the operator-created monthly price, portal, webhook registration, and three deployment secrets in `BLOCKERS.md`. |
| 7.6 | ✅ **DONE (2026-07-31)** — public CV (`/cv/{guid}`), privacy-first random links, revoke/rotate sharing, recruiter PDF download | **P3** | **M** | 3.4, 4.2 | Anonymous rendering is isolated behind an explicit public flag, served with `noindex`; revoked links cannot be restored accidentally, and rate-limited PDF export uses the same visibility check. |
| 7.7 | ✅ **DONE (2026-07-30)** — three free CV themes plus five Premium themes, enforced by account entitlement and clearly locked in the picker | **P3** | **S** | 4.3, 7.2 | Existing Premium-theme CVs remain editable and exportable after downgrade so user data is never held hostage. |
| 7.8 | **DONE (2026-07-31)** — CI runs NuGet transitive vulnerability reporting and blocks high/critical npm findings across production and test/browser tooling. The documented React Router baseline is moderate. | **P2** | **S** | none | Vulnerable dependencies are visible and high-severity regressions stop deployment. |
| 7.9 | ✅ **DONE (2026-07-30)** — per-user monthly AI token ceilings (100k free, 1M Premium/Admin) enforced alongside generation limits and exposed in usage totals | **P3** | **S** | 5.2, 7.2 | Existing metering is the single accounting source; paid-provider spend now has both request and token ceilings. |
---
## Maintenance milestone — Technical-debt reduction ✅ DONE (2026-07-31)
Removed the dead 232 MB vendor snapshot and transitional link-compilation project; consolidated API
source ownership; added Problem Details, traceable structured logs, trusted proxy handling, durable CV
queue restart recovery, CV artifact cleanup, PDF retention, and warning-free frontend tests; replaced
placeholder docs and reconciled stale architecture records. Remaining conditional debt and its trigger
conditions are tracked in `docs/architecture/technical-debt.md`.
---
## Critical path
All implementation dependencies through Phase 6 are closed. The remaining release path is:
`production key rotation → backup/restore rehearsal → deploy/smoke test → registration verification`
Stripe code is complete; activation can follow once the product, monthly price, portal, webhook, and credentials exist.
---
## Effort summary
| Phase | Rough size | Note |
|---|---|---|
| 0 — Foundation | **done** | Delivered 2026-07-17. |
| 1 — Critical fixes | **complete** | DataProtection rotation confirmed 2026-08-02. |
| 2 — UX | **done** | Delivered 2026-07-30. |
| 3 — Career Workspace | **done** | Delivered 2026-07-30. |
| 4 — CV Builder | **done** | Delivered 2026-07-30; public PDF completed 2026-07-31. |
| 5 — AI | **done** | Metering and quotas included. |
| 6 — Job discovery | **done** | Official NAV feed plus reviewed save flow. |
| 7 — SaaS | **operationally blocked** | Remote CI, Stripe activation, and interactive production signup/OAuth verification remain. |
| Maintenance — Technical debt | **done** | Conditional future debt remains trigger-based, not current blocking work. |
---
## 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
The authoritative list is `BLOCKERS.md`. Repository implementation is currently blocked only by
external systems, credentials/configuration, or production access: remote CI, Stripe activation, production signup/OAuth verification,
DataProtection key rotation, production verification/deployment, and the production-backed legacy
job/application cutover. Job-specific learning paths are implemented, and portfolio content stays in public CVs.
## Open questions raised by Phase 0
None remain. Career Workspace data was recovered into the relational model and the Kanban groups
prospect stages explicitly.