22 KiB
P7-09 — Job import compatibility prerequisite
Candidate evidence; final acceptance and publication pending. This is the bounded import prerequisite within P7-09 Settings, Connections and data lifecycle, followed by the remaining data/privacy/plan/backup presentation. It is not completion of that second boundary or of Phase 7. No backend/schema, provider, restore, production or Phase 8 capability is added.
Original defect and scope
The existing JSON jobs export calls JsonSerializer.SerializeToUtf8Bytes(jobs) without a camel-case naming policy. Its exported model members include Company, JobTitle and the remaining PascalCase job fields. The prior import owner cast parsed JSON to a TypeScript type, then read only j.company and j.jobTitle. The cast supplied no runtime conversion or validation. The ordinary RED records a company request with undefined and missing job fields for an app-export-shaped fixture. The prior camelCase path also omitted supported create fields, including structured compensation and description metadata.
The repair accepts the app's PascalCase export and the existing camelCase import shape. It normalizes only supported create fields, validates the complete array before making any create request, and retains the existing serial POST /companies then POST /jobapplications workflow. Export serialization, CSV generation/conversion, API ownership/defaults and endpoint semantics remain at their current owners. This is creation of new jobs from supported data, not restoration of an account export.
| Boundary | Candidate behavior |
|---|---|
| Root and row validation | Require an array of non-array row objects, a company object with a nonblank string name, and a nonblank string job title. Reject malformed JSON and invalid known fields before the first write, including an invalid later row. |
| Casing | Recognize each supported field's camelCase and PascalCase spelling. Equivalent known aliases are accepted; conflicting aliases fail preflight instead of silently choosing one. |
| Company payload | Forward only name, optional location and optional source. Exported company IDs, ownership and unrelated private/history fields are excluded. |
| Job text payload | Preserve supplied status, location, salary, salaryCurrency, salaryPeriod, nextAction, notes, description, translatedDescription, descriptionLanguage, tags, coverLetterText, jobUrl, source and countryCode, alongside required jobTitle. |
| Numeric/date payload | Preserve valid supplied salaryMin/salaryMax, including zero and supported finite decimal values, and supported dateApplied, followUpAt, feedbackRequestedAt and deadline values. Preflight also rejects the characterized decimal/DateTime converter-envelope failures below. Validate known types/calendar values without inventing a timezone or rewriting stored text. Omitted fields remain omitted; supported explicit nulls remain explicit. Server validation/normalization remains authoritative. |
| Server-owned state | Ignore exported IDs, owners, original company associations, saved timestamps, deletion state, derived display/response flags, history, messages/events, attachments and CV associations. Use the company ID returned by the current authenticated create request. |
| Admission and feedback | Reserve one component-local import synchronously during file reading and serial writes. Present checking/progress, acknowledged completion count, invalid-row guidance or stopped-row guidance. No automatic mutation retry. |
Canonical Career Profile data, CV variants/overrides/revisions, frozen package associations, document binaries and PDF pagination are outside this importer. None is reconstructed or mutated to make the round trip appear complete.
Converter-envelope follow-up
A separate trust-boundary review found that JavaScript-finite numbers and JavaScript-parseable dates can still fail the server's JSON converters after earlier rows have committed. An offline probe loaded the installed System.Text.Json 9.0.19 assembly in hosted PowerShell/.NET 10 to characterize those converter cases. That is converter characterization, not execution of the API host/runtime, a backend test suite or HTTP end-to-end evidence.
Two additional ordinary regressions cover the bounded repair:
- Salary values must be null or finite JavaScript numbers with
abs(value) < 2^96. The .NET decimal maximum is2^96 - 1, but JavaScript rounds that upper boundary to2^96; rejecting equality prevents forwarding the rounded, converter-invalid value. Positive controls retain zero, supported negative values and a representable value immediately below the characterized upper edge. - Supported date text has at most 16 fractional-second digits and numeric offsets no greater than
±14:00(14hours requires zero offset minutes). Explicit numeric offsets must also resolve within UTC years 0001–9999. That UTC-range check applies only when an explicit numeric offset is present. No-zone minimum/maximum values remain accepted unchanged with their unspecified local-time meaning; no timezone is added to make them pass. Positive controls retain0001-01-01T00:00:00Z,9999-12-31T23:59:59Z, a+14:00value, 16 fractional digits and no-zone0001-01-01T00:00/9999-12-31T23:59.
These checks reject the reproduced converter failures during whole-array preflight. They do not replace backend business validation, establish full converter parity across environments, add transactional behavior or alter existing request/session ownership. The original RED and the strengthened RED with explicit no-zone positive controls are both retained below. The latter is the exact final-test precursor to the converter GREEN.
Partial commits and uncertain outcomes
Whole-array preflight prevents known invalid input from starting a partial batch; it does not make the later HTTP requests transactional. Company creation can complete before its job request fails. Earlier job requests can complete before a later row fails. The displayed count advances only after a job response is accepted, so it represents acknowledged completions; a lost response can leave an additional server commit uncertain.
There is no rollback, company cleanup, creation-intent idempotency key, deduplication, resumable import or automatic replay. A local admission mutex does not resolve cross-tab/server concurrency or a lost response. Users must inspect Jobs before explicitly retrying an interrupted or failed import; re-importing can create additional resources. The server's authenticated ownership, validation and persistence contracts remain unchanged. This qualification retains RD21 and RD28.
Settings lifetime foundation
SettingsView adds an opt-in lazy keepMounted option to its existing TabPanel; General alone opts in. After activation, an inactive retained panel has HTML hidden. Its controls are absent from the active accessibility/keyboard flow, while its existing child owners keep their own state and request lifetimes. No save owner, global store, localStorage draft, route blocker or import queue is introduced.
An accepted import therefore continues when the user switches General → Notifications/Rules → General. The real ImportExportJobs owner retains its synchronous admission, progress and result. The existing privacy owner also retains its unsaved choice and initial read across these internal tabs. This is a lifetime foundation, not a repair or acceptance of the remaining privacy save/error behavior.
Leaving the Settings route actually unmounts the children. ImportExportJobs checks its mounted generation and current session before each subsequent write and before later feedback. Existing session invalidation, cookie/context checks and API cancellation remain unchanged; an old account cannot obtain a new account's write context. These checks stop queued work and ignore late client continuation, but cannot undo an HTTP request already committed by the server.
Rules and Notifications retain their existing parent-owned draft/save contracts and conditional panel behavior. Backup remains conditional, mounts only on explicit entry, and unmounts when leaving its tab. This foundation does not retain Backup or qualify pending account ZIP completion; that remains in the later approved data lifecycle work.
After the lifetime pair finished, the owner appended the following scope warning to the shared catalog:
Keep Settings open until the import finishes. If you leave, the import may be incomplete.
The paired Bokmål warning is:
Hold Innstillinger åpent til importen er ferdig. Hvis du går ut, kan importen bli ufullstendig.
“Leave” means leaving the Settings session/route; the retained General panel allows internal section switches. The preceding catalog copy also states that IDs, owners, history, attachments and CV associations are not restored. The post-pair catalog SHA256 is 52cd246d3176096d8e7b9ff2de86d5b998d0d715bc2ac51c2a8151c0f5f1ad60; final validation must cover this later copy.
Measured regression evidence
All timestamps below are 2026-10-17 UTC. These are finite mocked-network frontend tests with the real component owners, not live HTTP persistence, native browser, human accessibility or production evidence. No skips, expected failures or relaxed assertions were introduced to obtain these results.
| Run | Exact recorded time | Result and scope |
|---|---|---|
| Import compatibility RED | Shell command 08:55:44.198675–08:55:51.702201 |
11 PASS / 8 FAIL of 19, three suites: new job-import regression plus existing CSV-export and file-picker cases. The app-export mismatch, validation, partial-count, admission and disposal failures remain recorded. |
| Import compatibility candidate GREEN | Shell command 13:33:47.272272–13:33:58.610639 |
19/19 PASS, the same three-suite scope. This is the importer candidate before the Settings lifetime pair. |
| Settings lifetime RED | Jest runner start 13:35:22.446; suite 13:35:23.184–13:35:30.313 |
0 PASS / 2 FAIL. Switching tabs disposes the accepted batch after its first company request; the privacy draft input is removed. Both are ordinary runtime failures. |
| Settings lifetime GREEN | Jest runner start 13:35:54.419; new suite 13:35:55.128–13:35:58.999; existing Settings suite 13:35:59.016–13:36:14.662 |
23/23 PASS: two new integration cases plus all 21 existing Settings cases. The new cases prove one completed two-job batch across internal tabs, result retention, actual unmount stopping a subsequent queued job, retained privacy draft/read through a Bokmål switch without writes, and lazy/disposable Backup reads. |
| Converter-envelope initial RED | Shell command 13:40:39.075122–13:40:48.966580 |
19 PASS / 2 FAIL of 21, three suites. Existing cases remain passing; two new converter-boundary regressions fail ordinarily. |
| Converter-envelope final-test RED | Shell command 13:41:47.755723–13:41:56.617918 |
19 PASS / 2 FAIL of 21, with the exact final test source and strengthened no-zone positive controls. The earlier RED remains historical evidence rather than being overwritten. |
| Converter-envelope GREEN | Shell command 13:42:19.716130–13:42:28.377505 |
21/21 PASS / 3 suites, covering the 11 new import cases and 10 existing CSV/picker cases. Product d6c34ac9…, test c10ea4ad… and the later warning catalog 52cd246d… are the as-run sources. This does not rerun the earlier Settings lifetime suite or qualify the final combined/native gates. |
The two lifetime commands, run from job-tracker-ui, were:
rtk proxy npm test -- --runInBand --runTestsByPath src/settings-import-lifetime.test.tsx --json --outputFile=../.artifacts/phase7/p7-09/settings-import-lifetime-red-jest.json
rtk proxy npm test -- --runInBand --runTestsByPath src/settings-import-lifetime.test.tsx src/settings-view.test.tsx --json --outputFile=../.artifacts/phase7/p7-09/settings-import-lifetime-green-jest.json
The compatibility lane's exact npm.cmd commands, source/post snapshots and counts are retained in its command/result files. Do not sum the overlapping Settings/consumer suites into a whole-suite result.
Source and raw identities
| Artifact/source | SHA256 |
|---|---|
| Importer original RED source | e47ab9ca06212d868f54d7e5a7d2e6a51f90574eaad4b8bd397d4489e3cee6a2 |
| Importer candidate used by both lifetime runs | 411e65bed101a05775e18cbda4c68ed14338b12adbc52f6eaf961b09a583c974 |
| Import regression source before converter-envelope additions | 142f2ead0c379eb95ebb7c05c50e238a161dd33c30e15cef85224bbb1ab24d22 |
| Importer after converter-envelope repair | d6c34ac9cf12b4351eaf010fa57469b5c811d82dae13360104f955fec9349fd3 |
| Exact final import regression source | c10ea4ad047ee4cd64d7a900f9f093ab002236b37156c7b02e37c9914bf54113 |
| SettingsView before lifetime repair | d1078255adfbe9ef3a20b87a7f3bbe78707561587e6cd1316afe9b89cae8b294 |
| SettingsView after lifetime repair | dbd611dd58b18bd9b48683f08a6d4af973374107d678c93fa148925c94037ed7 |
| New lifetime regression source, unchanged RED → GREEN | 24af2a5954533087ddbff40659ff9f8dcf70805435dca772314c2be7d8bf6133 |
| Existing Settings regression source | 1b5dfa2e75554ea53c462ec21c041f9761884390aace42d4f3a02a218b90db17 |
| Catalog used by these candidate runs, before the later warning | c7d0651b2ea0f479d70e651320f3fb95a8a72a1ca68bb35244bfe585c89ded34 |
Import RED capsule job-import/p709-job-import-red-20261017/results.json |
bb962cc4eb7ef4a0db3ac48ad926384631434a4c567fdb1d37e294cebe5caceb |
Import candidate GREEN capsule job-import/p709-job-import-candidate1-20261017/results.json |
ced734e77e6c34250a4b32d7572416afab30b8972c9c837e8ea6b35f39765d28 |
Lifetime RED raw settings-import-lifetime-red-jest.json |
571ea7a543ada3b2deb1454fe43db7b7829745b4ed62c0a6f9d13f324150fe9d |
Lifetime GREEN raw settings-import-lifetime-green-jest.json |
7df561d8e32982a990ab028da508cbcab42c1586576ba76e84b0c77ac2904594 |
Lifetime worker receipt settings-import-lifetime-worker-receipt.json |
dd7cb29b221b36ba92aebc00604c12b1e4ab00dae60c88f331426e86deb1171a |
Converter initial RED capsule job-import/p709-job-import-contract-red-20261017/results.json |
04862b10fe3754d2e6304eea6b9f2af20d6a08ee5e88605b2afe824a599c5ec9 |
Converter final-test RED capsule job-import/p709-job-import-contract-red2-20261017/results.json |
b8e265fe65932c8d4dba451b809fc1da7a04373caa213438e154d0223704ddb3 |
Converter GREEN capsule job-import/p709-job-import-contract-green-20261017/results.json |
a6fe9244232b40104ba6f844c6eacfd511dc4d23b8fa2d184869f0ba11db3b96 |
Converter GREEN raw job-import/p709-job-import-contract-green-20261017/p709-job-import-contract-green-20261017-20261017T134219Z.json |
0b85e4d9eaa0b4aeb90506e911ce6670f0f18b5edbc0c8ae2c7475c49c229199 |
Artifact paths in this table are relative to .artifacts/phase7/p7-09/. The import and converter capsules contain complete source/post snapshots with allSourcesMatch: true. The lifetime RED source record was captured before that run; its worker receipt was captured after GREEN and confirms that the test/importer/privacy/Backup/catalog bytes match the earlier record, except for the authorized SettingsView repair. That post-run receipt is not represented as a pre-run source capsule. The later warning catalog is covered by the converter GREEN but not by the earlier lifetime run. Final combined checks, browser export and publication are not certified by these historical candidate hashes.
Preservation, debt and remaining gates
The 57-group preservation matrix and retained debt register remain authoritative. This candidate changes no group to PASS: 56 PARTIAL / PRES-C11 DEFERRED remains the full-group disposition.
| Contract | Retained boundary / remaining qualification |
|---|---|
| PRES-J10 | Existing CSV/JSON export, conversion, picker activation and supported import/count behavior; complete real HTTP/native round trip and target-spreadsheet compatibility still require their own evidence. |
| PRES-E09 | Existing authenticated ownership, immutable page session/context and actual route/session disposal; unit mocks do not establish all deployed/BFCache/offline cases. |
| PRES-S03 / PRES-W11 | Existing General controls, Rules/Notifications independent owners, internal draft and language behavior; RD11/RD12/RD13 server root causes remain open. |
| PRES-S04 | Privacy draft survives internal tabs while the same card owns its data; privacy pending-save/error, usage/plan/billing presentation and provider qualifications remain separate work. |
| PRES-S05 | No account ZIP, encrypted backup, lifecycle, deletion or restore behavior is migrated here. Backup is verified to remain lazy/conditional only. Downloading a backup is not restore proof. |
Final applicable native round-trip/owner-isolation and controlled-failure journeys, responsive EN/NB/light/dark images, keyboard/focus/reflow and all-enabled axe results remain pending for this prerequisite. Final TypeScript, lint, production build, hygiene/document/secret checks, commit, normal development push and direct remote verification also remain pending in this candidate report. No such gate is claimed from the focused tests above.
Keep the manual acceptance requirements open: applicable M01–M12 operator/device results, especially M09 spoken names/errors/progress and M11 contextual contrast/forced colors, plus physical touch/software-keyboard, real zoom and production font/photo/print qualifications. M13 remains the separate deferred AI return obligation; do not execute it while P5-13 is withheld.
RD21 partial-write/concurrency/idempotency limits, RD24 billing/deletion/retention policy and RD28 evidence provenance remain explicit. The remaining data/privacy/plan/backup work must not activate billing, deletion, restore, model qualification or provider calls. Production remains NO-GO under RD01–RD05 and the existing ingress, complete backup/restore, effective configuration/schema and tested rollback requirements. Main/deployment and Phase 8 remain outside this import boundary.
Final import boundary — 20 October 2026
Implemented CONDITIONAL GO. This section supersedes the candidate's pending automated gates above. The owner subsequently authorized production promotion despite the retained release qualifications; that independent release investigation is recorded separately. Phase 7 and production acceptance remain distinct.
The importer now accepts the actual PascalCase export and supported legacy camelCase input through an explicit create-field allowlist. It validates the entire file before any write, excludes foreign IDs/owners/history/files/CV graphs, preserves converter-supported dates and decimals, counts acknowledged job writes only and stops after an uncertain write. There is no automatic replay or transactional-resume claim. Accepted imports and privacy drafts survive internal Settings tabs; actual route/session disposal stops subsequent queued work. Existing Rules ownership is retained.
A final demonstrated failure showed a valid JSON import incorrectly using the invalid-JSON toast after a network failure. Its strengthened existing regression failed ordinarily (10 passed, one failed), then passed after selecting the existing localized stopped-import message. The real-HTTP browser case verifies the accurate Bokmål toast while General is hidden and retains the durable partial-write warning on return. There are no new persistence semantics.
Fresh full frontend 837/837 tests, 97 suites PASS, no pending/todo/failure, at 07:07:06–07:13:22 UTC. Compared with release baseline 824/95, this boundary adds 11 import cases and two Settings-lifetime cases in two suites. Fresh full backend 1057/1057 PASS, zero skipped, includes the separately published three-case company compatibility repair, commit 5addcde1e22e9625d1b39df037a05b24c69c9cde. It is not counted as three new import UI tests.
Fresh native 25/25 PASS, zero skipped/flaky/retries, at 07:10:12–07:14:40 UTC: Companies six, import four, actual CSV consumer one, Preferences five, Settings/Rules consumers eight and ordinary P2-D01 one. The four new import browser cases derive an inventory of 335; this focused run is not the full 335-case integrated acceptance. App/browser TypeScript, lint and production build pass. Export identity is bu1vQUTbty4F3HzM8S37n. The portable results preserve command timestamps, result hashes and explicitly post-run source identities.
The four real-HTTP import cases cover actual export/import round trip, supported legacy graph exclusion and account disposal, whole-file preflight with zero writes, and committed-write/controlled-503 uncertainty with internal tab retention. Eleven retained PNGs and eleven all-enabled axe scans cover English/Bokmål, light/dark and 320, 390, 768, 1024, 1280, 1440 and 1920 widths. All scans report zero violations and one color-contrast incomplete group each. Triage retains invisible zero-count badges and overlapping/background text during fixed chrome/snackbar states as contextual manual contrast requirements; incompletes are not passing contrast checks. The 320px Bokmål partial-failure and 1440px English actual-round-trip images were visually inspected in this final run; the other nine images are retained automated evidence, not claimed as newly inspected. All eleven earlier-run image inspections remain historical evidence only.
Preservation remains 56 PARTIAL / C11 DEFERRED, 33 routes. PRES-J10, E09, S03, S04 and W11 gain the bounded automated evidence above; S05 backup/restore and the remainder of P7-09 remain pending. Screen-reader/device/real-zoom/font/photo/print requirements and RD11/RD12/RD13/RD21 remain open. P5-13 stays withheld. Phase 8 has not started. Publication requires the final hygiene gate and direct remote receipt recorded in the chronological commit ledger.