90 lines
6.3 KiB
Markdown
90 lines
6.3 KiB
Markdown
# Save and conflict contract
|
|
|
|
Implemented contract; final qualification is recorded separately in the dated acceptance addendum.
|
|
|
|
## Canonical Career Profile
|
|
|
|
`GET /api/career/profile` returns `{ profile, completeness, cvText, revision }`.
|
|
The profile comes from canonical relational children/LongTail with the existing legacy blob
|
|
fallback. A short read transaction keeps source, revision and raw CV text coherent. Reads do not
|
|
backfill/write unversioned children. The public owner-specific load remains read-only.
|
|
|
|
`revision` is `CareerProfile.Version`; 0 means there is no canonical row. It is independent of
|
|
the structured document's schema string `profile.version` and extraction metadata profileVersion.
|
|
|
|
| Writer | Required source precondition | Commit boundary |
|
|
| --- | --- | --- |
|
|
| PUT `/career/profile` | Body `expectedVersion` from the draft's GET | Canonical row, replace-all children, append-only history, Identity projection and optional raw text |
|
|
| POST `/career/profile/versions/{version}/restore` | Body `expectedVersion` for current canonical source, independent of selected historic version | Restored snapshot appended as a new guarded revision and projection |
|
|
| POST `/profile-cv/parse` | Body `expectedVersion` and source text | Applied extraction run, canonical aggregate, projection and import pointers |
|
|
| POST `/profile-cv/runs/{id}/accept` | Body `expectedVersion` from the actual reviewed diff | Reviewed merge, canonical aggregate, applied-run metadata and projection |
|
|
|
|
Missing or negative Career preconditions return 400. A valid stale revision returns 409 with
|
|
`code: revision_conflict`. Competing first saves use the existing unique owner constraint;
|
|
specific owner-duplicate errors become conflicts. There is no blind-save service overload or
|
|
automatic stale retry. Existing-row writes also compare the original version in EF's database
|
|
UPDATE predicate, protecting the interval after controller validation.
|
|
|
|
Controller transactions include Identity updates. A failed Identity result throws before commit;
|
|
canonical rows/children/history and import pointers roll back together. Failed tracked changes
|
|
are cleared, so a later SaveChanges cannot publish a rejected operation. Successful responses
|
|
are constructed within this boundary and returned only after commit. Post-commit parse-artifact
|
|
pruning is logged best-effort maintenance and cannot turn the committed save into an error.
|
|
|
|
Null/omitted `cvText` preserves imported raw text; whitespace explicitly clears it. Identity fields
|
|
remain owned by the identity endpoint. The obsolete `/auth/profile` CV projection fields now fail
|
|
closed with 400 and guidance to use `/career/profile`; identity-only updates remain supported.
|
|
|
|
## Job snapshots and related text
|
|
|
|
Job reads expose integer `version`. Workspace overview and cover-letter reads expose `revision`
|
|
for that same job row. Tailored CV draft GET/generation also returns its job revision.
|
|
|
|
| Writer | Required body field | Success |
|
|
| --- | --- | --- |
|
|
| PUT `/jobapplications/{id}` | `expectedVersion` from the full editor snapshot | Existing 204 status, `X-Job-Version` committed revision |
|
|
| PUT `/jobapplications/{id}/application-drafts` | `expectedVersion` with the answer/recruiter/notes draft source | 204 and `X-Job-Version` |
|
|
| PUT `/jobapplications/{id}/cover-letter` | `expectedVersion` with current text/history source | Cover-letter DTO including new revision |
|
|
| POST `/jobapplications/{id}/cover-letter/versions/{version}/restore` | `expectedVersion` for current job, independent of history version | Cover-letter DTO including new revision |
|
|
| PUT `/jobapplications/{id}/tailored-cv[-draft]` | `expectedVersion` with the legacy text/structured draft source | 204 and `X-Job-Version` |
|
|
|
|
Missing/invalid/stale job preconditions fail with 409. Every tracked JobApplication mutation,
|
|
including partial lifecycle and background writers, advances the application-managed version.
|
|
EF's original-version predicate guards the database write; full replacement/no-op saves force
|
|
that guard even if the submitted fields compare equal. An overlapping winner cannot be silently
|
|
replaced after a slow optional AI summary. Related opportunity fields and timeline events remain
|
|
inside the same SaveChanges transaction. No history/events are appended for a rejected write.
|
|
|
|
Generic job PUT still preserves the separately embedded application answer when editing ordinary
|
|
notes. It validates company ownership under tenant filters. Partial application-draft null fields
|
|
retain their prior semantics. Source guards do not alter CV-specific null-as-inherit overrides or
|
|
submitted/frozen documents.
|
|
|
|
## Client acknowledgement and recovery
|
|
|
|
Each draft retains the revision belonging to its actual source. A failed/409 save leaves user text
|
|
and choices available. The client never reads the latest revision just before PUT to authorize
|
|
an older body, never silently merges/retries a conflict, and never labels a rejected request saved.
|
|
The editor excludes overlapping mutations and late responses from older modal/read sessions.
|
|
|
|
Recovery requires deliberate reload/review. A network failure may leave commit outcome unknown;
|
|
the original revision cannot commit twice. Refresh and compare before resubmitting a retained
|
|
draft. This repair does not claim recovery of already overwritten historical edits.
|
|
|
|
## Compatibility and qualification
|
|
|
|
Older unversioned replacement clients intentionally fail closed. Active application callers and
|
|
test fixtures are updated; the extension/bookmarklet create flow does not perform these replacement
|
|
writes. An external client must read a source snapshot and carry its revision before replacing it.
|
|
No missing-revision compatibility fallback is accepted.
|
|
|
|
Migration `20261023090000_AddJobRevision` adds the job integer column with default 1. SQLite tests
|
|
upgrade populated synthetic storage and roll back/reapply while preserving job content. Career's
|
|
existing version column becomes an EF concurrency token without a new physical column. A schema
|
|
rollback does not preserve revision identity and is not a qualified production rollback procedure.
|
|
|
|
SQLite runtime tests and provider SQL generation do not prove MariaDB runtime behavior. The local
|
|
Docker Linux engine was unavailable. Mixed old/new server or client deployment, actual MariaDB,
|
|
production storage durability and release/rollback remain separate qualifications. No deployment
|
|
or production migration was performed.
|