Data Model
Entity Relationship
erDiagram
Job ||--o| JdAnalysis : analyzed
Job ||--o{ Application : applied
Resume ||--o{ TailoredResume : tailored
Resume ||--|{ ResumeSection : contains
Resume ||--o{ SkillEntry : "generates profile"
TailoredResume ||--|{ TailoredSection : contains
TailoredResume }o--|| Job : for
Application }o--o| TailoredResume : uses
Job {
uuid id PK
string platform
string platform_job_id
string title
string company
string location
int salary_min
int salary_max
string url
text description_raw
bool pr_required "nullable"
bool citizenship_required "nullable"
bool visa_sponsorship "nullable"
string status
datetime scraped_at
}
JdAnalysis {
uuid job_id FK
json required_skills
json preferred_skills
int years_experience
json keywords
float match_score
text match_reasoning
}
Resume {
uuid id PK
bool is_base
string format
}
ResumeSection {
uuid id PK
uuid resume_id FK
string section_type
text content
bool tailorable
}
TailoredResume {
uuid id PK
uuid base_resume_id FK
uuid job_id FK
text diff_summary
bool approved
}
Application {
uuid id PK
uuid job_id FK
uuid tailored_resume_id FK
string status
datetime applied_at
}
SkillEntry {
uuid id PK
string name
string category
int proficiency "1-10"
float years
json evidence
string source
}
Key Entities
Job
The central entity. Tracks a job posting from discovery through application.
#![allow(unused)]
fn main() {
pub enum Platform { Seek, LinkedIn, Indeed }
pub enum JobStatus {
New, // Just scraped
Analyzed, // JD analyzed by LLM
Matched, // User marked as interesting
Skipped, // User passed
Applied, // Submitted
Rejected, // Got rejection
Interview, // Interview scheduled
Offer, // Received offer
}
}
JdAnalysis
LLM-generated analysis attached 1:1 to a Job. Key fields:
required_skills/preferred_skills— extracted from JDkeywords— ATS keywords to include in resumematch_score— 0.0 to 1.0, computed against UserProfilematch_reasoning— human-readable explanation
Resume & TailoredResume
Base resume imported once and split into sections. Each section marked tailorable: true/false.
Tailored resumes only store modified sections — unchanged sections inherit from base at render time.
#![allow(unused)]
fn main() {
pub enum SectionType {
Summary, // tailorable
Education, // protected
WorkExperience, // tailorable (bullets only)
Internship, // tailorable (bullets only)
Projects, // tailorable
Technologies, // tailorable
Extracurricular, // protected
References, // protected
}
}
Application
Tracks the lifecycle from preparation to outcome.
#![allow(unused)]
fn main() {
pub enum ApplicationStatus {
Prepared, // Resume tailored, ready
Submitted, // User confirmed
Acknowledged, // Company acknowledged
Interview, // Scheduled
Rejected,
Offer,
}
}
Skill Profile
A structured representation of the user’s technical abilities, auto-generated from resume analysis and adjustable manually. Used by JD Analysis to compute precise match scores.
#![allow(unused)]
fn main() {
pub enum SkillCategory {
Language, // Rust, Python, TypeScript
Framework, // Django, React, axum
Database, // SQLite, PostgreSQL, SQL Server
Tool, // Docker, Git, CI/CD
Cloud, // AWS, GCP, Azure
Concept, // System Design, Async, REST API
}
pub enum SkillSource {
ResumeAnalysis, // Auto-extracted from resume by LLM
Manual, // User-provided
Both, // Auto-extracted then user-adjusted
}
}
Each SkillEntry includes:
proficiency— 1-10 scale (1 = awareness, 5 = working proficiency, 8 = expert, 10 = authority)years— years of experience with this skillevidence— specific projects/roles that demonstrate this skill (e.g. “Efision ERP — async/tokio/axum”)source— whether it was auto-extracted, manually added, or both
SQLite Schema
All JSON arrays (skills, keywords) stored as TEXT with serde_json. UUIDs as TEXT. Dates in ISO 8601.
See crates/kairos-db/migrations/V1__initial.sql for the full schema.