# 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` 2–4days · `L` ~1–2wk · `XL` 2wk+

**Current state:** Phases 0–7 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 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 | ✅ **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 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 | ✅ **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.
