Semantius Logo

Applicant Tracking System — Semantic Model

1. Overview

An applicant tracking system used by an in-house recruiting team to manage open job requisitions, the candidates considered for them, and the full hiring funnel from application through interviews to offer. Primary users are recruiters, hiring managers, and interviewers; the system records who applied for what, where they are in the pipeline, what feedback interviewers gave, and what offers were extended and accepted.

2. Entity summary

#Table nameSingular labelPurpose
1departmentsDepartmentOrganizational units that own job openings (e.g. Engineering, Sales). Supports an optional parent–child hierarchy.
2job_openingsJob OpeningA specific role being hired for, with status, hiring team, headcount, and target start date.
3application_stagesApplication StageConfigurable pipeline steps (e.g. New, Phone Screen, On-site, Offer, Hired, Rejected) with order and category.
4candidate_sourcesCandidate SourceWhere candidates come from (job board, referral, agency, inbound, sourced).
5candidatesCandidateA person in the talent pool. Exists independently of any specific job application.
6job_applicationsJob ApplicationA candidate applying to a specific job opening — the central pipeline record.
7candidate_documentsCandidate DocumentResumes, cover letters, portfolios, work samples attached to a candidate.
8application_notesApplication NoteComment thread on an application — recruiter and hiring-manager observations.
9interviewsInterviewA scheduled interview event tied to an application (kind, time, location/URL).
10interview_feedbackInterview FeedbackScorecard from one interviewer for one interview — rating, recommendation, notes.
11offersOfferA formal offer extended to a candidate for a job — terms, status, candidate response.
12hiring_team_membersHiring Team MemberJunction: a user assigned to a job opening with a role (recruiter, hiring manager, interviewer, coordinator).
13usersUserSystem users (recruiters, hiring managers, interviewers, coordinators). Modeled for self-containment; deployer dedupes against the Semantius built-in.

Entity-relationship diagram

flowchart LR
    departments -->|has children| departments
    users -->|heads| departments
    departments -->|employs| users
    departments -->|owns| job_openings
    users -->|manages| job_openings
    users -->|recruits for| job_openings
    candidate_sources -->|sources| candidates
    users -->|referred| candidates
    candidates -->|applies via| job_applications
    job_openings -->|receives| job_applications
    application_stages -->|stages| job_applications
    candidate_sources -->|sources| job_applications
    users -->|assigned to| job_applications
    candidates -->|owns| candidate_documents
    users -->|uploaded| candidate_documents
    job_applications -->|has| application_notes
    users -->|authored| application_notes
    job_applications -->|has| interviews
    users -->|coordinates| interviews
    interviews -->|has| interview_feedback
    users -->|gives| interview_feedback
    job_applications -->|has| offers
    users -->|approves| offers
    job_openings -->|staffed by| hiring_team_members
    users -->|on team| hiring_team_members

3. Entities

3.1 departments — Department

Plural label: Departments Label column: department_name Audit log: no Description: An organizational unit that owns one or more job openings. Supports an optional parent–child hierarchy so business units can contain sub-teams.

Fields

Field nameFormatRequiredLabelReference / Notes
department_namestringyesDepartment Namelabel_column; unique
department_codestringnoCodeunique (e.g. ENG, SALES)
parent_department_idreferencenoParent Departmentdepartments (N:1), self-reference for hierarchy
head_user_idreferencenoDepartment Headusers (N:1)

Relationships

  • A department may have a parent_department and many child departments (1:N self-reference, optional).
  • A department may be headed by one user (N:1, optional).
  • A department owns many job_openings (1:N, via job_openings.department_id).
  • Many users may belong to a department (1:N, via users.department_id).

3.2 job_openings — Job Opening

Plural label: Job Openings Label column: job_title Audit log: yes (status changes and salary band updates are subject to dispute; keep a history) Description: A specific position the company is hiring for. Created in draft, opened for applications, then transitions through on_hold / filled / closed / cancelled. Has one hiring manager and an optional lead recruiter; additional team members are tracked via hiring_team_members.

Fields

Field nameFormatRequiredLabelReference / Notes
job_titlestringyesJob Titlelabel_column
job_codestringnoRequisition Codeunique (e.g. ENG-2026-014)
department_idreferenceyesDepartmentdepartments (N:1, restrict)
hiring_manager_idreferenceyesHiring Managerusers (N:1, restrict)
recruiter_idreferencenoLead Recruiterusers (N:1, clear)
employment_typeenumyesEmployment Typevalues: full_time, part_time, contract, internship, temporary
work_arrangementenumyesWork Arrangementvalues: onsite, remote, hybrid
locationstringnoLocation
statusenumyesStatusvalues: draft, open, on_hold, filled, closed, cancelled
headcountintegeryesHeadcounthow many to hire
opened_atdatenoOpened
target_start_datedatenoTarget Start Date
filled_atdatenoFilled
salary_minfloatnoSalary Min
salary_maxfloatnoSalary Max
salary_currencystringnoCurrencyISO 4217 code (e.g. USD)
job_descriptionhtmlnoDescriptionrich-text role description
job_requirementstextnoRequirementsrequired experience, skills, etc.

Relationships

  • A job_opening belongs to one department (N:1, required, restrict).
  • A job_opening has one hiring_manager user (N:1, required, restrict) and optionally one lead recruiter user (N:1, optional, clear).
  • A job_opening receives many job_applications (1:N, via job_applications.job_opening_id, restrict — applications must be archived before a job can be deleted).
  • A job_opening has many hiring_team_members (1:N, via the hiring_team_members junction).

3.3 application_stages — Application Stage

Plural label: Application Stages Label column: stage_name Audit log: no Description: A configurable step in the application pipeline. Stages are shared across all job openings (single global pipeline assumption — see §6.1). stage_order controls sort; stage_category groups stages so reports and downstream logic can reason about pipeline phase without parsing names.

Fields

Field nameFormatRequiredLabelReference / Notes
stage_namestringyesStage Namelabel_column; unique
stage_orderintegeryesOrdersort order in the pipeline
stage_categoryenumyesCategoryvalues: pre_screen, screening, interview, offer, hired, rejected
is_activebooleanyesActivedefault true
descriptiontextnoDescription

Relationships

  • An application_stage may be the current stage of many job_applications (1:N, via job_applications.current_stage_id, restrict — stages cannot be deleted while in use).

3.4 candidate_sources — Candidate Source

Plural label: Candidate Sources Label column: source_name Audit log: no Description: A named source from which candidates and applications originate, used for sourcing analytics. Examples: “LinkedIn Jobs”, “Employee Referral Program”, “Acme Recruiting Agency”.

Fields

Field nameFormatRequiredLabelReference / Notes
source_namestringyesSource Namelabel_column; unique
source_typeenumyesSource Typevalues: job_board, referral, agency, inbound, sourced, social_media, career_site, event, other
is_activebooleanyesActivedefault true
descriptiontextnoDescription

Relationships

  • A candidate_source may be the source of many candidates (1:N, via candidates.source_id, clear).
  • A candidate_source may be the source of many job_applications (1:N, via job_applications.source_id, clear) — the application can be tagged with a different source than the candidate (e.g. candidate originally sourced from LinkedIn, but applied via referral for this specific role).

3.5 candidates — Candidate

Plural label: Candidates Label column: full_name Audit log: yes (personal data subject to GDPR / data-subject access requests; preserve a change history) Description: A person in the talent pool. A candidate exists independently of any specific job and may have multiple job_applications over time. Identity is loosely keyed on email_address (unique when present) — duplicates are detected at the recruiter’s discretion.

Fields

Field nameFormatRequiredLabelReference / Notes
full_namestringyesFull Namelabel_column
first_namestringnoFirst Name
last_namestringnoLast Name
email_addressemailnoEmailunique
phone_numberstringnoPhone
linkedin_urlurlnoLinkedIn
current_employerstringnoCurrent Employer
current_job_titlestringnoCurrent Job Title
location_citystringnoCity
location_countrystringnoCountry
source_idreferencenoSourcecandidate_sources (N:1, clear)
referrer_user_idreferencenoReferred Byusers (N:1, clear) — the employee who made the referral, when source is a referral
candidate_statusenumyesCandidate Statusvalues: active, hired, archived, do_not_contact
notestextnoNotescandidate-level notes (vs application-level)

Relationships

  • A candidate may originate from one candidate_source (N:1, optional, clear).
  • A candidate may be referred by one user (N:1, optional, clear).
  • A candidate owns many job_applications (1:N, via job_applications.candidate_id, cascade — deleting a candidate wipes their applications, supporting GDPR erasure).
  • A candidate owns many candidate_documents (1:N, via candidate_documents.candidate_id, cascade).

3.6 job_applications — Job Application

Plural label: Job Applications Label column: application_label Audit log: yes (stage transitions and status changes are central to the audit trail of a hiring decision) Description: The central pipeline record — a specific candidate applying to a specific job opening, currently sitting at one application stage. Created when a candidate applies (or when a recruiter pulls them into a role) and progresses through stages until terminal status (hired, rejected, withdrawn).

Fields

Field nameFormatRequiredLabelReference / Notes
application_labelstringyesApplicationlabel_column; caller composes on insert (e.g. "{candidate.full_name} → {job_opening.job_title}")
candidate_idparentyesCandidatecandidates (N:1, cascade)
job_opening_idreferenceyesJob Openingjob_openings (N:1, restrict) — preserves history if a job is closed
current_stage_idreferenceyesCurrent Stageapplication_stages (N:1, restrict)
statusenumyesStatusvalues: active, hired, rejected, withdrawn, on_hold
source_idreferencenoSourcecandidate_sources (N:1, clear)
applied_atdate-timeyesApplied At
assigned_recruiter_idreferencenoAssigned Recruiterusers (N:1, clear)
rejection_reasonenumnoRejection Reasonvalues: not_qualified, withdrew, position_filled, no_show, salary_mismatch, location_mismatch, culture_fit, other
rejected_atdate-timenoRejected At
hired_atdate-timenoHired At

Relationships

  • A job_application belongs to one candidate as its parent (N:1, required, cascade — owning lifecycle).
  • A job_application references one job_opening (N:1, required, restrict — historical applications survive a job closure).
  • A job_application is currently at one application_stage (N:1, required, restrict).
  • A job_application may originate from one candidate_source (N:1, optional, clear).
  • A job_application may be assigned to one user recruiter (N:1, optional, clear).
  • A job_application has many application_notes, interviews, and offers (1:N, all cascade except offers which is restrict — see §3.11).

3.7 candidate_documents — Candidate Document

Plural label: Candidate Documents Label column: document_label Audit log: no Description: A document attached to a candidate — resume, cover letter, portfolio, work sample, certification, or reference letter. Stored as a URL pointing to file storage; the model does not own the binary itself.

Fields

Field nameFormatRequiredLabelReference / Notes
document_labelstringyesDocumentlabel_column; caller composes (e.g. "Resume — Jane Doe")
candidate_idparentyesCandidatecandidates (N:1, cascade)
document_typeenumyesDocument Typevalues: resume, cover_letter, portfolio, work_sample, certification, reference_letter, other
file_urlurlyesFile URLexternal storage URL
file_namestringnoFile Nameoriginal uploaded filename
uploaded_atdate-timeyesUploaded At
uploaded_by_user_idreferencenoUploaded Byusers (N:1, clear)

Relationships

  • A candidate_document belongs to one candidate as its parent (N:1, required, cascade — documents are wiped if the candidate is erased).
  • A candidate_document may be uploaded by one user (N:1, optional, clear).

3.8 application_notes — Application Note

Plural label: Application Notes Label column: note_subject Audit log: no Description: A note left on an application — a recruiter or hiring-manager observation, decision rationale, or coordination message. Visibility controls who can read the note (whole hiring team vs. recruiters only vs. publicly visible to the candidate).

Fields

Field nameFormatRequiredLabelReference / Notes
note_subjectstringyesSubjectlabel_column; short summary line
application_idparentyesApplicationjob_applications (N:1, cascade)
author_user_idreferenceyesAuthorusers (N:1, restrict) — preserves authorship audit trail
note_bodytextyesNote
visibilityenumyesVisibilityvalues: hiring_team, recruiter_only, public
noted_atdate-timeyesNoted At

Relationships

  • An application_note belongs to one job_application as its parent (N:1, required, cascade).
  • An application_note is authored by one user (N:1, required, restrict — author cannot be deleted while their notes exist).

3.9 interviews — Interview

Plural label: Interviews Label column: interview_label Audit log: no Description: A scheduled interview event for a specific application. May involve one or more interviewers (each captured as their own interview_feedback row). Status transitions from scheduled through completed / cancelled / no_show / rescheduled.

Fields

Field nameFormatRequiredLabelReference / Notes
interview_labelstringyesInterviewlabel_column; caller composes (e.g. "Tech Phone Screen — Jane Doe")
application_idparentyesApplicationjob_applications (N:1, cascade)
interview_kindenumyesKindvalues: phone_screen, video_call, onsite, technical, take_home, panel, final, reference_check
scheduled_startdate-timeyesStart
scheduled_enddate-timeyesEnd
locationstringnoLocationphysical location for onsite interviews
meeting_urlurlnoMeeting URLvideo-call link
statusenumyesStatusvalues: scheduled, completed, cancelled, no_show, rescheduled
coordinator_user_idreferencenoCoordinatorusers (N:1, clear)

Relationships

  • An interview belongs to one job_application as its parent (N:1, required, cascade).
  • An interview may be coordinated by one user (N:1, optional, clear).
  • An interview has many interview_feedback rows — one per interviewer (1:N, via interview_feedback.interview_id, cascade).

3.10 interview_feedback — Interview Feedback

Plural label: Interview Feedback Label column: feedback_label Audit log: yes (scorecards are decision evidence; preserve change history) Description: One interviewer’s scorecard for one interview. An interview can have multiple feedback rows when multiple people attended (e.g. a panel). is_submitted distinguishes a draft scorecard from a finalized one.

Fields

Field nameFormatRequiredLabelReference / Notes
feedback_labelstringyesFeedbacklabel_column; caller composes (e.g. "Alex Kim — Tech Phone Screen for Jane Doe")
interview_idparentyesInterviewinterviews (N:1, cascade)
interviewer_user_idreferenceyesInterviewerusers (N:1, restrict) — preserves authorship
overall_ratingenumnoOverall Ratingvalues: strong_yes, yes, lean_yes, lean_no, no, strong_no
recommendationenumnoRecommendationvalues: advance, hold, reject
strengthstextnoStrengths
concernstextnoConcerns
detailed_notestextnoDetailed Notes
is_submittedbooleanyesSubmitteddefault false
submitted_atdate-timenoSubmitted Atpopulated when is_submitted flips to true

Relationships

  • An interview_feedback belongs to one interview as its parent (N:1, required, cascade).
  • An interview_feedback is authored by one user interviewer (N:1, required, restrict — the interviewer cannot be deleted while feedback exists).

3.11 offers — Offer

Plural label: Offers Label column: offer_label Audit log: yes (offers are commitments — preserve full change history of terms, status, and approvals) Description: A formal offer extended to a candidate for a specific application. Goes through draftpending_approvalapprovedsent, then accepted / declined / rescinded / expired. An application typically has at most one active offer; the model uses restrict so an offer is never silently lost when an application is cleaned up.

Fields

Field nameFormatRequiredLabelReference / Notes
offer_labelstringyesOfferlabel_column; caller composes (e.g. "Offer — Jane Doe — Senior Engineer")
application_idreferenceyesApplicationjob_applications (N:1, restrict)
statusenumyesStatusvalues: draft, pending_approval, approved, sent, accepted, declined, rescinded, expired
base_salaryfloatyesBase Salary
salary_currencystringyesCurrencyISO 4217 code
bonus_targetfloatnoBonus Targetannual on-target bonus
equity_amountstringnoEquityfree-text (shares, RSU value, percentages vary)
start_datedatenoStart Dateproposed start date
offer_extended_atdate-timenoExtended Attimestamp the offer was sent to the candidate
offer_expires_atdate-timenoExpires At
candidate_responseenumyesCandidate Responsevalues: pending, accepted, declined, no_response
responded_atdate-timenoResponded At
approver_user_idreferencenoApproverusers (N:1, clear)

Relationships

  • An offer references one job_application (N:1, required, restrict — preserves the offer record even if cleanup of the application is attempted).
  • An offer may have one approving user (N:1, optional, clear).

3.12 hiring_team_members — Hiring Team Member

Plural label: Hiring Team Members Label column: team_member_label Audit log: no Description: Junction associating a user with a job_opening in a specific role (recruiter, hiring manager, interviewer, coordinator, executive sponsor). A user can sit on many openings; an opening can have many team members in the same or different roles. The hiring manager and lead recruiter on job_openings are summary FKs for the most-common case; this junction holds the full team and additional roles.

Fields

Field nameFormatRequiredLabelReference / Notes
team_member_labelstringyesTeam Memberlabel_column; caller composes (e.g. "Alex Kim — Hiring Manager — Senior Engineer")
job_opening_idparentyesJob Openingjob_openings (N:1, cascade)
user_idparentyesUserusers (N:1, cascade)
team_roleenumyesRolevalues: recruiter, hiring_manager, interviewer, coordinator, executive_sponsor
assigned_atdate-timeyesAssigned At
is_activebooleanyesActivedefault true — set false to remove from team without deleting history

Relationships

  • A hiring_team_member belongs to one job_opening and one user, both as parents (cascade on either side).
  • usersjob_openings is many-to-many through this junction.

3.13 users — User

Plural label: Users Label column: display_name Audit log: no Description: A system user — recruiter, hiring manager, interviewer, or coordinator. Modeled here for self-containment. Semantius ships a built-in users table; the deployer reuses the built-in and only adds any of the fields below that the built-in lacks. All other entities reference this table via reference_table: "users".

Fields

Field nameFormatRequiredLabelReference / Notes
display_namestringyesDisplay Namelabel_column
email_addressemailyesEmailunique
first_namestringnoFirst Name
last_namestringnoLast Name
job_titlestringnoJob Titlethis user’s own job title at the company
department_idreferencenoDepartmentdepartments (N:1, clear)
is_activebooleanyesActivedefault true

Relationships

  • A user may belong to one department (N:1, optional, clear).
  • A user may head one or more departments (1:N, via departments.head_user_id).
  • A user may be the hiring manager, lead recruiter, assigned recruiter, coordinator, interviewer, author, uploader, or approver across many other entities — see §4 for the full edge list.
  • usersjob_openings is many-to-many through hiring_team_members.

4. Relationship summary

FromFieldToCardinalityKindDelete behavior
departmentsparent_department_iddepartmentsN:1referenceclear
departmentshead_user_idusersN:1referenceclear
usersdepartment_iddepartmentsN:1referenceclear
job_openingsdepartment_iddepartmentsN:1referencerestrict
job_openingshiring_manager_idusersN:1referencerestrict
job_openingsrecruiter_idusersN:1referenceclear
candidatessource_idcandidate_sourcesN:1referenceclear
candidatesreferrer_user_idusersN:1referenceclear
job_applicationscandidate_idcandidatesN:1parentcascade
job_applicationsjob_opening_idjob_openingsN:1referencerestrict
job_applicationscurrent_stage_idapplication_stagesN:1referencerestrict
job_applicationssource_idcandidate_sourcesN:1referenceclear
job_applicationsassigned_recruiter_idusersN:1referenceclear
candidate_documentscandidate_idcandidatesN:1parentcascade
candidate_documentsuploaded_by_user_idusersN:1referenceclear
application_notesapplication_idjob_applicationsN:1parentcascade
application_notesauthor_user_idusersN:1referencerestrict
interviewsapplication_idjob_applicationsN:1parentcascade
interviewscoordinator_user_idusersN:1referenceclear
interview_feedbackinterview_idinterviewsN:1parentcascade
interview_feedbackinterviewer_user_idusersN:1referencerestrict
offersapplication_idjob_applicationsN:1referencerestrict
offersapprover_user_idusersN:1referenceclear
hiring_team_membersjob_opening_idjob_openingsN:1parent (junction)cascade
hiring_team_membersuser_idusersN:1parent (junction)cascade

5. Enumerations

5.1 job_openings.employment_type

  • full_time
  • part_time
  • contract
  • internship
  • temporary

5.2 job_openings.work_arrangement

  • onsite
  • remote
  • hybrid

5.3 job_openings.status

  • draft
  • open
  • on_hold
  • filled
  • closed
  • cancelled

5.4 application_stages.stage_category

  • pre_screen
  • screening
  • interview
  • offer
  • hired
  • rejected

5.5 candidate_sources.source_type

  • job_board
  • referral
  • agency
  • inbound
  • sourced
  • social_media
  • career_site
  • event
  • other

5.6 candidates.candidate_status

  • active
  • hired
  • archived
  • do_not_contact

5.7 job_applications.status

  • active
  • hired
  • rejected
  • withdrawn
  • on_hold

5.8 job_applications.rejection_reason

  • not_qualified
  • withdrew
  • position_filled
  • no_show
  • salary_mismatch
  • location_mismatch
  • culture_fit
  • other

5.9 candidate_documents.document_type

  • resume
  • cover_letter
  • portfolio
  • work_sample
  • certification
  • reference_letter
  • other

5.10 application_notes.visibility

  • hiring_team
  • recruiter_only
  • public

5.11 interviews.interview_kind

  • phone_screen
  • video_call
  • onsite
  • technical
  • take_home
  • panel
  • final
  • reference_check

5.12 interviews.status

  • scheduled
  • completed
  • cancelled
  • no_show
  • rescheduled

5.13 interview_feedback.overall_rating

  • strong_yes
  • yes
  • lean_yes
  • lean_no
  • no
  • strong_no

5.14 interview_feedback.recommendation

  • advance
  • hold
  • reject

5.15 offers.status

  • draft
  • pending_approval
  • approved
  • sent
  • accepted
  • declined
  • rescinded
  • expired

5.16 offers.candidate_response

  • pending
  • accepted
  • declined
  • no_response

5.17 hiring_team_members.team_role

  • recruiter
  • hiring_manager
  • interviewer
  • coordinator
  • executive_sponsor

6. Open questions

6.1 🔴 Decisions needed (blockers)

None.

6.2 🟡 Future considerations (deferred scope)

  • Should application_stages be per-job-opening rather than global, so different role types (engineering vs sales vs executive) can have different pipelines? Adding a job_pipelines link entity and a pipeline_id on job_openings would model this cleanly.
  • Should candidates be linked to a structured skills taxonomy (M:N via candidate_skills), or is the unstructured notes field sufficient for v1?
  • Should rejection reasons be promoted from an enum to a configurable lookup table (rejection_reasons) if recruiters want to add custom reasons over time?
  • Should email and calendar integration produce an application_activities / email_messages log so all candidate communication is captured against the application? Out of scope for v1.
  • Should offer approval be modeled as a multi-step approval workflow (e.g. an offer_approvals entity with one row per approver) rather than a single approver_user_id and pending_approval status?
  • Should salary_currency be promoted from a free-text ISO code to a lookup table or enum once the company hires across multiple geographies?
  • Should the model track GDPR consent and retention dates explicitly on candidates (e.g. consent_given_at, retention_expires_at) so automated purging is possible?

7. Implementation notes for the downstream agent

  1. Create one module named ats (the module name must equal the system_slug from the front-matter — do not rename) and two baseline permissions (ats:read, ats:manage) before any entity.
  2. Create entities in dependency order — referenced entities first. Suggested order:
    1. users (deduped against built-in — see step 6)
    2. departments (self-references and references users; create the entity, then add the self-ref FK after the entity exists)
    3. application_stages
    4. candidate_sources
    5. candidates
    6. job_openings
    7. job_applications
    8. candidate_documents
    9. application_notes
    10. interviews
    11. interview_feedback
    12. offers
    13. hiring_team_members
  3. For each entity: set label_column to the snake_case field marked as label in §3, pass module_id, view_permission: "ats:read", edit_permission: "ats:manage". Set audit_log: true on job_openings, candidates, job_applications, interview_feedback, and offers (per §3). Do not manually create id, created_at, updated_at, or the auto-label field.
  4. For each field in §3: pass table_name, field_name, format, title (the Label column), and for reference/parent fields also reference_table and a reference_delete_mode consistent with §4. The §3 Required column is analyst intent; the platform manages nullability internally and does not need a per-field flag. Pass enum_values for every enum field, taken from §5. Set unique_value: true on departments.department_name, departments.department_code, job_openings.job_code, application_stages.stage_name, candidate_sources.source_name, candidates.email_address, and users.email_address.
  5. Fix up each entity’s auto-created label-column field title. create_entity auto-creates a field whose field_name equals the entity’s label_column and whose title defaults to singular_label. For most entities in this model, the §3 Label of the label_column field differs from singular_label and the title must be patched with update_field. The update_field id is the composite string "{table_name}.{field_name}" — pass it as a string, not an integer. Required updates:
    • "departments.department_name" → title "Department Name"
    • "job_openings.job_title" → title "Job Title"
    • "application_stages.stage_name" → title "Stage Name"
    • "candidate_sources.source_name" → title "Source Name"
    • "candidates.full_name" → title "Full Name"
    • "job_applications.application_label" → title "Application"
    • "candidate_documents.document_label" → title "Document"
    • "application_notes.note_subject" → title "Subject"
    • "interview_feedback.feedback_label" → title "Feedback"
    • "hiring_team_members.team_member_label" → title "Team Member"
    • "users.display_name" → title "Display Name"
    • (interviews.interview_label and offers.offer_label already match singular_label “Interview” / “Offer” — no fixup needed.)
  6. Deduplicate against Semantius built-in tables. This model declares users for self-containment. Semantius ships a built-in users table — read it first; if it exists, skip the create and reuse the built-in as the reference_table target everywhere this model points to users. Optionally add any of the §3.13 fields the built-in lacks (display_name, email_address, first_name, last_name, job_title, department_id, is_active) — additive low-risk only. Do not attempt to recreate users. The departments, roles, permissions, etc. listed in §3 are not Semantius built-ins and should be created normally.
  7. After creation, spot-check that label_column on each entity resolves to a real field, that all reference_table targets exist, that the parent_department_id self-reference on departments was successfully added after the entity was created, and that the two junction FKs on hiring_team_members resolve correctly.