Files
jobtrackingapp/docs/redesign/phase-7/p7-09/IMPORT_COMPATIBILITY.md
T

22 KiB
Raw Blame History

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 is 2^96 - 1, but JavaScript rounds that upper boundary to 2^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 (14 hours 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 retain 0001-01-01T00:00:00Z, 9999-12-31T23:59:59Z, a +14:00 value, 16 fractional digits and no-zone 0001-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.