# Job opportunity cutover The release-readiness build closes the write-side gap: - normal application creation and Gmail suggested-job creation both create a linked `Job`; - edits synchronize opportunity fields through `JobOpportunitySync`; - startup idempotently creates one `Job` for every legacy `JobApplication` whose `JobId` is null. The startup backfill is additive and runs after the deployment backup. It does not drop or overwrite legacy application columns. ## Production validation Run these read-only checks after deployment. Expected result for every count is `0`: ```sql SELECT COUNT(*) AS missing_job FROM JobApplications WHERE JobId IS NULL; SELECT COUNT(*) AS missing_target FROM JobApplications a LEFT JOIN Jobs j ON j.Id = a.JobId WHERE a.JobId IS NOT NULL AND j.Id IS NULL; SELECT COUNT(*) AS owner_mismatch FROM JobApplications a JOIN Jobs j ON j.Id = a.JobId WHERE NOT (a.OwnerUserId <=> j.OwnerUserId); SELECT COUNT(*) AS field_mismatch FROM JobApplications a JOIN Jobs j ON j.Id = a.JobId WHERE NOT (a.CompanyId <=> j.CompanyId) OR NOT (a.JobTitle <=> j.JobTitle) OR NOT (a.Location <=> j.Location) OR NOT (a.JobUrl <=> j.JobUrl) OR NOT (a.Description <=> j.Description) OR NOT (a.Salary <=> j.Salary); ``` `<=>` is MariaDB/MySQL's null-safe equality operator. Do not use these statements as an update script. ## Expand/contract release order 1. Deploy the additive backfill and synchronized writers. 2. Capture the validation counts above and observe one release. 3. Change reads to use `Job`; keep compatibility columns during that release. 4. Re-run validation against a fresh backup restore. 5. Only then make `JobId` required and drop duplicated opportunity columns in a later migration. Do not combine the destructive column drop with the first production backfill.