feat(workspace): application assets workflow
Phase 5.4. Connects the career outputs a user already has to one job
application, without building a second copy of any of them.
The flow is strictly one-directional — CareerProfile -> CvVariant ->
application output — and nothing writes back up. No code path in this phase
touches CareerProfile or its children.
CV integration re-points rather than duplicates. GET/PUT /{id}/cv attaches one
variant to an application via CvVariant.JobApplicationId; replacing detaches the
previous variant instead of deleting it. Creating, duplicating, editing, theming,
previewing, exporting PDF and version history all stay in the existing CV
builder, which the section links into. There is no second CV system.
Tailoring composes the Phase 5.3 analysis and match into skills to highlight,
experience to prioritise, projects to emphasise, keywords to include and gaps to
address. Deterministic and advisory: it says what the user could emphasise and
the user edits the variant themselves. Nothing auto-applies.
Cover letters gain the history they were missing. JobApplication.CoverLetterText
stays the current text with its API contract unchanged; CoverLetterVersions
records what it used to be, so an AI rewrite is never destructive. Restore is
additive — the old text comes back as a new version, so what you restored from
still exists. Source and AiAction record whether the user wrote a version or
approved it from a suggestion, and an AI generation only becomes a version once
the user saves it.
Documents are untouched: the existing Attachment system already covers CV, cover
letter, certificates and portfolio files with a Purpose field, so the workspace
mounts that component rather than adding a second upload path.
CoverLetterVersions is the only new table — reconciler-owned, no-op migration,
guarded on JobApplications, and verified on a fresh MariaDB 11: int
AUTO_INCREMENT primary key, varchar(255) owner, datetime(6), composite index
inside the key limit.
360 backend tests, 115 frontend tests, type check, Release build and the
production build all pass locally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
namespace JobTrackerApi.Models;
|
||||
|
||||
// Phase 5.4 — Application Assets.
|
||||
//
|
||||
// Append-only history for a job application's cover letter. JobApplication.CoverLetterText stays the
|
||||
// CURRENT text and the existing API contract around it is unchanged; this only records what it used
|
||||
// to be, so an AI rewrite or a bad edit is never destructive. Mirrors CvVariantVersion: restore
|
||||
// re-saves an old snapshot as a new version rather than rewinding.
|
||||
//
|
||||
// Reconciler-owned (docs/infrastructure/database-ownership.md) — its migration is a no-op.
|
||||
public sealed class CoverLetterVersion
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string OwnerUserId { get; set; } = string.Empty;
|
||||
public int JobApplicationId { get; set; }
|
||||
public JobApplication? JobApplication { get; set; }
|
||||
|
||||
public int Version { get; set; }
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
// manual | ai | template | restore — how this text came to exist, so the history list can show
|
||||
// whether the user wrote it or approved it from a suggestion.
|
||||
public string Source { get; set; } = "manual";
|
||||
|
||||
// For an AI-sourced version: which action produced it (generate | improve | shorten | expand |
|
||||
// tone | tailor). Null for anything the user typed.
|
||||
public string? AiAction { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
public static class CoverLetterSources
|
||||
{
|
||||
public const string Manual = "manual";
|
||||
public const string Ai = "ai";
|
||||
public const string Template = "template";
|
||||
public const string Restore = "restore";
|
||||
|
||||
public static bool IsValid(string? value) =>
|
||||
value is Manual or Ai or Template or Restore;
|
||||
}
|
||||
Reference in New Issue
Block a user