Every JobApplication gets a dedicated workspace at /applications/{id} — a
surface, not a new data store. It owns no data and duplicates none: CV comes
from the Phase 4 CvVariant lens, analysis/match/interview from the existing
AiWorkspacePanel, documents from Attachments, communication from
Correspondence, activity from JobEvent, stage semantics from JobPipeline. No
career data is copied and nothing here writes.
- GET /api/jobapplications/{id}/workspace: one aggregate read (role, company,
stage, dates, attached CV variant, cover letter, documents, AI history,
recent activity) replacing the page fanning out across endpoints
- Next recommended action: ordered rules answering "what do I do next?", the
core product principle for this phase
- ApplicationWorkspacePage: left nav + linkable ?section=, reusing the existing
component for each domain; later-milestone sections say so rather than faking
- Entry point from the job dialog via an optional onOpenWorkspace callback —
the dialog must not depend on router context (it is mounted without a
<Router> in several suites), so the caller owns navigation
- 8 backend tests (aggregate, CV variant surfacing, counts, activity ordering,
next-step rules, tenant scoping)
Local: 314 backend, 88/88 frontend (31 suites), tsc clean, production build ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.3 KiB
Application Workspace (Phase 5)
Phase 5, Milestone 1 (2026-07-18). The per-application workspace: what it is, what it deliberately is not, and how it composes existing systems. Companion to
cv-builder.md,ai-career-assistant.md,career-profile-model.md.
What it is
A dedicated surface for one JobApplication at /applications/{id}, so an application is a place you
work rather than a row you edit in a modal. Job tracking stays the product; the workspace is the
application's home.
Core principle: the user should never ask "what do I do next?" — the overview always answers it.
What it is NOT
The workspace owns no data and duplicates none. It is an aggregate read plus a navigation shell:
| Section | Backed by (existing system) |
|---|---|
| CV | Phase 4 CvVariant — a lens over the master CareerProfile |
| Analysis / Match / Interview | Phase 5 AiWorkspacePanel + AiInteraction history |
| Documents | Attachment |
| Communication | Correspondence |
| Activity / Timeline | JobEvent |
| Stage semantics | JobPipeline |
No career data is copied into the application. Nothing in this feature writes to the master profile, a CV variant, or a cover letter.
Backend
GET /api/jobapplications/{id}/workspace → WorkspaceOverviewDto
(ApplicationWorkspaceController + ApplicationWorkspaceService).
One aggregate read instead of the page fanning out: role/company/location/salary, status + pipeline
group, applied/deadline/follow-up dates, the attached CV variant (id, name, theme), cover-letter
presence, document count, AI interaction count + last run, recent JobEvent activity, and the
computed next recommended action.
Read-only and tenant-scoped (OwnerUserId), returning 404 for another user's application.
Next recommended action
First unmet rule wins, ordered to match where the application actually is — understand the role, prepare the material, send it, then chase it:
add-job-details— no advert text (analysis and matching need it)prepare-cv— no CV variant attached and no tailored CV textwrite-cover-letter— no cover letterattach-documents— nothing attachedprepare-interview— the application reached an interview stageschedule-follow-up— applied with no follow-up dateset-next-action— no next action written
Prospect-stage applications short-circuit to CV → cover letter → submit. null means nothing is
outstanding.
Frontend
ApplicationWorkspacePage (/applications/:id) — a left nav plus a content pane, section selected by
?section=, so a section is linkable and survives refresh. Reached from the job dialog's "Open
application workspace" button.
The dialog passes an optional onOpenWorkspace callback rather than calling useNavigate itself:
JobDetailsDialog must stay renderable without a <Router> (several suites mount it standalone), so
router context belongs to the caller.
Implemented now: Overview, Job Details, and the sections that reuse an existing component
(Analysis/Match/Interview → AiWorkspacePanel, Documents → Attachments,
Communication → Correspondence). Sections owned by later milestones state their milestone instead of
faking functionality.
Relationship to /readiness
GET /{id}/readiness already computes a polish-oriented checklist (score, completed, missing,
reminders) and still backs the dialog's Readiness tab. The workspace's next-action rules are
deliberately narrower and action-shaped. Milestone 2 introduces the persisted, user-editable checklist
and folds the readiness signals into it as defaults — at which point the overlap is resolved in one
place rather than two.
Extension points
- New section: add to
WORKSPACE_SECTIONSand render it; nav is data-driven. - New next-action rule: add one clause to
ApplicationWorkspaceService.NextStep— ordered, so position is the priority. - More overview data: extend
WorkspaceOverviewDto; the page reads one payload.
Milestones
- ✅ Workspace foundation — route, nav shell, aggregate overview, next recommended action.
- Checklist and progress tracking (persisted + custom items).
- Timeline and activity history. 4. Job analysis. 5. Career matching. 6. CV integration.
- Cover letter workflow. 8. Documents. 9. Interview preparation. 10. Dashboard improvements.