feat(api): link a Job/Contract's employer to a network Organization (#375) #376
No reviewers
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol!376
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "375-job-org-link-backend"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Backend for #375 — tie a company you've worked at to a tracked network Organization, mirroring the people↔organizations link-or-stub pattern. A job is 1:1 with an employer, so it's a nullable FK column on
jobs, not a junction. The client UI (link picker + "your roles here") is a tracked follow-up.What's in it
jobs.organization_idnullable FK →organizations(id)ON DELETE SET NULL. Existing jobs stay unlinked free-text — no backfill.LEFT JOIN organizations(JobWithOrg→ DTOorganizationName); rename the org → the job reflects it.companyis kept as a fallback snapshot, refreshed to the org name at link time.OrganizationsRepository.deleteById): snapshot the org name into linked jobs'company+ null the link before deleting the org, so career history degrades to a stub with no data loss. The FKSET NULLis the Postgres safety net; the app-level copy-down is the real mechanism (SQLite FK enforcement is off) and runs on both engines — two plain statements (no transaction, mirroringconversations.deletefor the libsql:memory:leg).lib/api/job-organization-link.ts): a cross-user/missing org id → 404 (don't leak existence); applies to both jobs and contracts. On a valid link,companyis set to the org's name (fresh snapshot).GET /api/organizations/{id}/roleslists the linked jobs/roles.idea.mdupdated.Verification (I re-ran all of it, incl. the Postgres leg)
testagainst both engines (ephemeral Postgres): 1257 passed, 0 skipped — the migration applies on Postgres and the new dual-engine tests cover link, unlink, live-name-on-rename, org-delete copy-down,listByOrganizationIdscoping, cross-user 404, and contract linking.typecheck✓ ·lint✓ ·openapi:check✓ / coverage 122 ·@carol/api-client check✓ · semgrep ✓ 0 findings on the new files.Follow-up (rest of #375)
/roles).link_job_to_orgtool (mirroringlink_person_to_org).Part of #375.
🤖 Generated with Claude Code
Backend for tying a company you've worked at to a tracked Organization, mirroring the people↔organizations link-or-stub pattern. A job is 1:1 with an employer, so it's a nullable FK column on jobs, not a junction. - Migration 026: jobs.organization_id nullable FK → organizations(id) ON DELETE SET NULL. Existing jobs stay unlinked free-text — no backfill. - Linked jobs resolve the org's LIVE name on read (LEFT JOIN organizations → JobWithOrg / DTO organizationName); company is kept as a fallback snapshot, refreshed to the org's name at link time. - Org delete = copy-down (OrganizationsRepository.deleteById): snapshot the org name into linked jobs' company + null the link before deleting, so career history degrades to a stub with no data loss. FK SET NULL is the Postgres safety net; the app-level copy-down is the real mechanism (SQLite FK enforcement is off), runs on both engines. - Link validation (lib/api/job-organization-link.ts): a cross-user/missing org id → 404 (don't leak existence); applies to both jobs and contracts. - Reverse view: GET /api/organizations/{id}/roles lists the linked jobs/roles (for the org detail's "your roles here"). - zod/openapi + api-client regenerated (coverage 121 → 122); idea.md updated. Dual-engine tests: link, unlink, live-name-on-rename, org-delete copy-down, listByOrganizationId scoping, cross-user 404, contract linking. Part of #375 (client UI — link picker + "roles here" — is a follow-up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>📊 Test coverage
Patch coverage: no testable lines changed.
Overall (
app/,lib/,db/, excluding UI per ADR-0019):Soft thresholds per ADR-0019. Coverage is informational and does not block merge.