Files
jobtrackingapp/docs/cv-import/BACKEND.md
T

10 KiB

Career Profile import contracts

26 October 2026. CareerProfileService remains the only canonical profile/history/revision writer. No CV variant, letter, submitted package, application association or frozen document is rewritten by import.

API and lifecycle

  • POST /api/profile-cv/import: authenticated Pro PDF/DOCX upload, 5 MiB limit, file signature/archive validation, safe owned artifact name and SHA-256, existing durable cv.process admission. New runs use document_import / docling-v1. Legacy /upload remains available until replacement qualification.
  • GET /runs/{id}/review: owned source text, original filename, warnings, current canonical revision and explicit fields grouped by supported Career Profile category. Each field has current/imported values and source method/snippet/page. Metadata does not substitute for authorization.
  • GET /runs/{id}/source: authenticated owner-scoped original-file response with private/no-store and nosniff headers. Frontend fetches an authenticated blob; token credentials are never placed in an iframe URL.
  • POST /runs/{id}/apply-reviewed: expectedVersion plus allowlisted decisions {id,action,value?}. Keep skips a field; use/add accepts its extracted value; correct records an explicit user correction. Missing decisions are omitted, never implicitly accepted. An empty selected set is refused and the user can discard instead. Legacy /accept refuses docling-v1 runs.

The existing queue supplies admission limits, ownership scope, durable operation status, attempts/deadlines, cancellation, recovery and retry. Real extraction/interpretation/readiness stages are persisted, with no simulated percent. Heartbeats preserve the latest reported stage and cancel execution if renewal fails. Final publication checks the active operation lease/cancellation state in the database predicate. Recovery qualification is distinct from actual model quality.

The additive 20261026090000_AddCvImportReviewEvidence migration adds nullable DocumentJson and ReviewJson to existing extraction runs. No second master profile, queue, storage root or revision counter is introduced. Account export serializes extraction records including these columns; account deletion already deletes owned runs/artifacts and files.

Supported fields and evidence

Canonical StructuredCvProfile is authoritative: contact (including GitHub and labelled links), summary, jobs, education, certifications, projects, skills, languages, interests, awards, publications, organisations, references and custom sections. Courses and other unsupported typed domains remain custom section items. Existing schema has no separate experience-duration or arbitrary qualification records.

Source / canonical mapping Current deterministic interpretation Limit
contact.fullName/headline/email/phone/location/website/linkedIn/gitHub/links EN/NB labelled contact lines; email regex; Docling title as name; labelled portfolio link Unlabelled phone/location/title may remain unclassified
summary[] Recognized summary heading, original text No generated professional background
jobs[].title/company/start/end/isCurrent/bullets Explicit pipe-delimited title/employer/date record; following lines as details Ordinary unlabelled multiline title/employer/date layouts may remain custom source; job location/skills are not inferred
education[].qualification/institution/start/end/details Explicit delimited qualification/institution/date record Specialism can remain original detail; no inferred degree level/location
certifications[].name/issuer/date Delimited source values PDF table cells can become separate proposed names; review warning is mandatory
projects[].name/role/bullets Delimited named record / following text Project dates/location/skills not inferred
skills[], languages[].name/level Explicit lists and language:level No skill attribution from unrelated prose; missing level null
interests/awards/publications/organisations/references[] Explicit recognized section lines No reclassification of unfamiliar headings based on guesses
otherSections[].title/items[] Courses, unfamiliar/ambiguous/unclassified source Review can retain/correct/skip; reclassifying to a missing typed record requires existing Career Profile editing

This is a deliberately limited deterministic grammar, not broad qualification of arbitrary conventional CV layouts. The review/schema layer supports all existing canonical fields, but automatic extraction does not populate every possible field. Missing typed records cannot currently be created inside import review. These are explicit PARTIAL mapping/review acceptance items for CV-QA-01, separate from completed transaction and approval safety.

CvDocumentInterpreter interprets structural blocks/headings and labelled fields conservatively. Clear delimited employment/education records retain source date strings and current markers. Unknown employment stays as reviewable source rather than inventing a role or employer. Missing language proficiency remains null. Unclassified source remains visible. The current deterministic grammar is bounded; unconventionally arranged documents may require corrections. Native PDF table structure is explicitly limited.

CvDocumentReview.Ground removes unsupported candidate strings, refuses inferred date precision and attaches source block/page/snippet to significant fields. Fixed heuristic confidence percentages are not used in this path. Current employment is a labelled normalization of a supported source marker. The original extraction is stored on the run; approved/corrected profile metadata and the actual reviewed decisions preserve their origin. No AI-generated facts enter the active deterministic path.

Merge and revision safety

Review IDs are generated from the typed extracted profile; callers cannot submit arbitrary JSON patches. Invalid/duplicate/excessive paths/actions and invalid corrections are refused. Existing profile validation and a serialized-size bound apply before persistence.

Jobs match only on complete normalized employer/title/start/end; education on institution/qualification/start/end; certificates on name/issuer/date; projects on name/role/start/end. Incomplete or conflicting identifiers are conservatively new records. Similar titles alone never trigger an update. Language names and custom-section titles are category-specific matches. Duplicate existing matches remain new/reviewable rather than choosing an arbitrary record. List entries append uniquely, and unrelated contact links are preserved by labelled matching. Existing values absent from the upload remain untouched.

Apply loads the canonical revision and writes profile, relational children, append-only history, Identity projection and run acknowledgement inside the existing atomic transaction. Identity/projection failure rolls everything back and clears tracked mutations. A stale expected revision returns 409. A repeated identical applied request returns its original recorded acknowledgement without creating another profile version; different decisions on an applied run return conflict. Stable IDs remain assigned by the existing owner. CV overrides, inheritance and frozen documents remain outside this transaction's write scope.

Source retention

The new hosted retention sweep runs on startup and hourly. New import source files last at most seven days, or one day after failed/cancelled/discarded work, subject to sweep timing. Abandoned review/processing expires; the user must reupload. Late operations are revoked before source deletion. Files must resolve within the configured CV artifact root; failed deletions retain their database reference for retry. Canonical approved facts/history are retained according to existing account policies, and are removed through existing account-deletion controls. Pending review is no longer silently pruned by the generic 20-completed-run history cap.

Evidence and retained failures

  • Entry provider: 64/64 actual MariaDB PASS.
  • provider-review-01: FAIL, new redundant startup repair called a SQLite-only helper on MariaDB. Removed the repair; the additive migration owns these columns.
  • provider-review-02: 67/69; current migration expectation and rollback-detached test entity fixture were corrected without relaxing behavior.
  • provider-review-03: 69/69 actual MariaDB PASS, no skips; includes new reviewed application/rollback/idempotency/stale/ownership/down-up cases, plus retained RD21/letter/frozen-package coverage. Generated service cleanup exited zero.
  • parser-red-01: three real structural-parser failures before the block interpreter; successor parser-green-01 passed 46 focused cases.
  • retention-green-01: 50 focused cases PASS. link-merge-red reproduced unrelated-link replacement; review-safe-green-01 passed 51 focused cases after correction.

Focused runs overlap full regression and each other. They must never be added together. Final source-bound regression/provider decisions and the fixture-only recovery accounting are recorded in ACCEPTANCE.md.

Provider and review addendum

provider-review-04 reproduced a canonical/projection revision discrepancy: legacy Identity counter 99 caused imported metadata 100 while the canonical revision was 2. Apply now derives both from the canonical snapshot. provider-review-05 did not run tests because its nested-shell environment assignment failed; it is not a provider result. provider-review-06 passed 70/70, zero skips, actual MariaDB 12.2.2, cleanup exit 0. This includes preservation of unrelated existing summary/contact/custom facts and canonical revision metadata. The successful review endpoint acknowledgement follows the committed transaction.

Lease checks now protect initial promotion, final publication and queue status synchronization. A superseded worker cannot reset a recovered pending review. All open pending reviews remain accessible beyond the latest-ten history slice. Grounded profile serialization avoids legacy heuristic enrichment. Field provenance carries the original artifact/run/SHA identities through merges and later client saves.