Gig::Application
Purpose
An Application represents a talent's intent to work a specific shift, combined with the mutual selection process between employer and talent. The talent shows they want to work the shift by applying, and the employer shows they want that talent by accepting.
Applications belong to the recruiting phase of the gig lifecycle. They answer the question: "Who wants to work this shift, and do both parties agree?" Once both parties agree (the "double handshake"), the Application's job is done and a Gig::Assignment is created for the operations phase.
Applications also carry the talent's ranking data — a score computed from show-up reliability, cancellation history, category experience, and employer ratings. Employers see applicants ordered by rank, and the auto-selection system uses ranking to pick the best candidates when the employer hasn't selected anyone by the deadline.
Model Context
| Context | Details |
|---|---|
| Aggregate | Gig::Application (independent — child of Shift) |
| Layer | Recruiting |
| Upstream dependencies | Gig::Shift (which shift the talent is applying to), Talent::Profile (who is applying) |
| Downstream dependents | Gig::Assignment (created when application reaches confirmed status) |
State Machine
| From | To | Trigger | Notes |
|---|---|---|---|
| (new) | pending | UC-1: Talent applies | Default status. Talent is in the applicant pool. |
pending | accepted | UC-3: Employer accepts | Employer selected this talent. Waiting for talent to confirm. |
pending | accepted | UC-4: Auto-selection selects | System auto-selected this talent. Still needs talent confirmation. |
pending | rejected | UC-5: Employer rejects | Employer does not want this talent. Reason required. |
pending | withdrawn | UC-6: Talent withdraws | Talent changed their mind before selection. No penalty. |
pending | expired | UC-8: Shift cutoff reached | Shift expired or cutoff passed without this talent being selected. |
pending | cancelled | UC-9: Shift cancelled | Employer cancelled the entire shift. |
accepted | confirmed | UC-7: Talent confirms | Double handshake complete. System creates Gig::Assignment. |
accepted | expired | UC-8: Talent confirmation deadline passes | Talent didn't confirm in time. Employer can select the next ranked applicant. |
accepted | declined | UC-7: Talent explicitly declines | Talent was selected but says no. No penalty (they were asked, not committed). |
accepted | cancelled | UC-9: Shift cancelled | Employer cancelled the entire shift. |
confirmed | cancelled | Assignment-level cancellation | If the resulting Assignment is cancelled, the Application reflects this. |
Use Cases
| ID | Use Case | Trigger | Actor |
|---|---|---|---|
| UC-1 | Talent applies to a Shift | Talent wants to work this shift | Talent |
| UC-2 | System ranks applicants for a Shift | New application received or ranking refresh needed | System |
| UC-3 | Employer accepts one or more applicants | Employer selects workers from the ranked list | Employer |
| UC-4 | System auto-selects top-ranked applicants | Auto-select deadline reached, employer hasn't selected | System |
| UC-5 | Employer rejects an applicant | Employer does not want this talent for the shift | Employer |
| UC-6 | Talent withdraws application before selection | Talent changed their mind | Talent |
| UC-7 | Talent confirms or declines acceptance | Talent responds to selection notification | Talent |
| UC-8 | Application expires | Shift cutoff or confirmation deadline reached | System |
| UC-9 | Application cancelled due to shift cancellation | Employer cancels the shift | System |
| UC-10 | Time overlap auto-rejection | Talent confirmed for a conflicting shift | System |
| UC-11 | View applicants for a Shift | Employer reviewing who applied | Employer |
UC-1: Talent applies to a Shift
| Field | Details |
|---|---|
| Actor | Talent::Profile (talent) |
| Trigger | Talent wants to work this shift |
Preconditions:
Gig::Shiftexists andstatus: open- Shift headcount is not yet full (
filled_count < headcount) - Talent is not suspended (no active suspension)
- Talent does not have a confirmed Assignment that overlaps with this shift's time
- Talent has not already applied to this shift
System Behavior:
- Talent selects a Shift from the marketplace (via
Listings::Job) - System validates preconditions (listed above)
- System creates
Gig::Applicationwithstatus: pending - System triggers ranking recalculation for all
pendingApplications on this Shift (UC-2)
Business Rules:
- One Application per talent per shift — a talent cannot apply twice to the same shift
- Applying does not guarantee selection — the talent enters the ranked applicant pool
- Time overlap check is against confirmed Assignments only (not other pending Applications)
- Suspended talent cannot apply
Postconditions:
- Application exists with
status: pending - Talent appears in the employer's applicant list for this shift
UC-2: System ranks applicants for a Shift
| Field | Details |
|---|---|
| Actor | System |
| Trigger | New application received, or periodic ranking refresh |
Preconditions:
- Shift has at least one
pendingApplication
System Behavior:
- System evaluates each
pendingapplicant using a weighted scoring algorithm:- Show-up reliability (30%): based on historical no-show and completion rates
- Cancellation history (25%): frequency and timing of past cancellations
- Category experience (25%): completed shifts in the same job role/category
- Employer ratings (20%): average ratings from previous employers
- System assigns a
talent_rank(1 = best) and stores the score breakdown intalent_rank_detail(JSON) - Ranking is recalculated when a new application arrives or when existing applicants' statuses change
Business Rules:
- Ranking applies only to
pendingApplications — accepted/confirmed/rejected applications are not re-ranked - New talent with no history receive a baseline score (not penalised for being new, but ranked below proven workers)
- Ranking detail is stored as JSON for transparency: employers and ops can see why a talent is ranked where they are
Postconditions:
- All
pendingApplications on the Shift have updatedtalent_rankandtalent_rank_detail
UC-3: Employer accepts one or more applicants
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer selects workers from the ranked applicant list |
Preconditions:
- Shift
status: open - At least one Application in
pendingstatus - Number of selections does not exceed remaining headcount (
headcount - filled_count)
System Behavior:
- Employer selects one or more applicants from the ranked list
- For each selected applicant:
Gig::Application.status→accepted - Each accepted talent receives a notification: "You've been selected for [shift details]. Please confirm."
- Remaining
pendingapplicants stay in the pool — they may be selected later if an accepted talent declines or expires
Business Rules:
- Employer can accept up to
headcount - filled_countapplicants at a time - Acceptance does NOT create an Assignment — the talent must still confirm (double handshake)
- The employer can accept applicants in multiple batches (e.g., accept 2 now, accept 1 more tomorrow)
Postconditions:
- Selected Applications are
accepted - Talent notified, awaiting confirmation
UC-4: System auto-selects top-ranked applicants
| Field | Details |
|---|---|
| Actor | System |
| Trigger | Auto-select deadline reached, employer hasn't selected anyone |
Preconditions:
- Auto-selection is enabled for this location (per-location configuration)
- Shift
status: open - No Applications are in
acceptedorconfirmedstatus (employer hasn't acted) - Auto-select deadline has arrived:
- AM shifts (starting 00:00–11:59): 9:00am the previous day
- PM shifts (starting 12:00–23:59): 5:00pm the previous day
System Behavior:
- System retrieves all
pendingApplications, ordered bytalent_rankascending - System selects the top N applicants where N =
headcount - For each: validates no time overlap, talent not suspended
- If a talent fails validation, skip to the next ranked applicant
- Each valid applicant:
Gig::Application.status→accepted - Remaining unselected Applications:
Gig::Application.status→rejected(reason: auto-selection — not selected) - Each accepted talent receives a notification
Business Rules:
- Auto-selection only runs if NO applicant has been accepted or confirmed — if the employer has already selected at least one person, auto-selection does not trigger
- Auto-selection still requires talent confirmation (double handshake) — it does not skip to Assignment
- If there are fewer eligible applicants than headcount, the system selects all available
Postconditions:
- Top-ranked Applications are
accepted - Remaining Applications are
rejected - Talent notified, awaiting confirmation
UC-5: Employer rejects an applicant
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer does not want this talent for the shift |
Preconditions:
- Application
status: pending
System Behavior:
- Employer rejects the applicant with a mandatory reason (via
Taxonomy::GigStatusReason) Gig::Application.status→rejected- Talent is notified
Business Rules:
- Rejection reason is mandatory
- Rejection is final — the talent cannot re-apply to the same shift
- Rejection does not affect the talent's ranking or suspension status (it's the employer's choice, not a penalty)
Postconditions:
- Application
status: rejected - Talent notified
UC-6: Talent withdraws application before selection
| Field | Details |
|---|---|
| Actor | Talent::Profile (talent) |
| Trigger | Talent changed their mind before selection |
Preconditions:
- Application
status: pending
System Behavior:
- Talent requests withdrawal
Gig::Application.status→withdrawn- System recalculates rankings for remaining applicants
Business Rules:
- No penalty, no document required — the talent was not yet committed
- Cancellation reason is optional
- The talent can apply to other shifts freely
Postconditions:
- Application
status: withdrawn - Shift's applicant count decreases
UC-7: Talent confirms or declines acceptance
| Field | Details |
|---|---|
| Actor | Talent::Profile (talent) |
| Trigger | Talent responds to selection notification |
Preconditions:
- Application
status: accepted - Confirmation deadline has not passed
System Behavior — Confirms:
- Talent confirms: "Yes, I will work this shift"
Gig::Application.status→confirmed- System creates
Gig::Assignmentwithstatus: confirmed - System checks for time overlaps with the talent's other pending/accepted Applications → conflicting Applications auto-rejected (UC-10)
- Shift's filled count increases
System Behavior — Declines:
- Talent declines: "No, I can't work this shift"
Gig::Application.status→declined- The position opens up — employer can select the next ranked applicant
- Talent is not penalised (they were asked, not committed)
Business Rules:
- Confirmation must happen within a deadline (configurable — e.g., 24 hours after acceptance, or a fixed time before shift start)
- Declining is free — no penalty, no suspension impact. The talent was offered the position, they said no.
- Upon confirmation, the double handshake is complete and the Assignment is created immediately
Postconditions (confirmed):
- Application
status: confirmed Gig::Assignmentcreated- Time overlap check executed
Postconditions (declined):
- Application
status: declined - Position available for next applicant
UC-8: Application expires
| Field | Details |
|---|---|
| Actor | System |
| Trigger | Deadline reached without action |
Preconditions:
- Application
status: pending(shift cutoff reached) orstatus: accepted(talent didn't confirm)
System Behavior — Pending application expires:
- Shift cutoff time reached
- All remaining
pendingApplications →expired
System Behavior — Accepted application expires:
- Talent confirmation deadline passes
Gig::Application.status→expired- The position opens up — employer or auto-selection can pick the next applicant
- Talent is notified: "Your selection for [shift] has expired because you did not confirm in time"
Business Rules:
- Expiration is not a penalty — the talent is not penalised for not confirming (they may not have seen the notification)
- Expired accepted applications free up the position for the next ranked applicant
Postconditions:
- Application
status: expired
UC-9: Application cancelled due to shift cancellation
| Field | Details |
|---|---|
| Actor | System |
| Trigger | Employer cancels the shift |
Preconditions:
- Application in any non-terminal status (
pending,accepted,confirmed)
System Behavior:
- Employer cancels the Shift (see
Gig::ShiftUC-4) - All Applications in
pending,accepted, orconfirmedstatus →cancelled - Talent are notified: "The shift you applied for / were selected for has been cancelled"
Business Rules:
- This is a cascading effect of shift cancellation, not a direct action on the Application
- Talent are not penalised — the cancellation was employer-initiated
Postconditions:
- All non-terminal Applications for this shift are
cancelled
UC-10: Time overlap auto-rejection
| Field | Details |
|---|---|
| Actor | System |
| Trigger | Talent confirms an Assignment for a shift that overlaps with other Applications |
Preconditions:
- Talent just confirmed an Application (UC-7), creating an Assignment
- Talent has other
pendingoracceptedApplications for shifts that overlap in time
System Behavior:
- System scans the talent's other Applications
- For each Application where the shift's
(starts_at, ends_at)overlaps with the newly confirmed shift:Gig::Application.status→rejected(reason: time overlap)
- Talent is notified: "Your application for [Shift B] was automatically withdrawn due to a scheduling conflict with [Shift A]"
Business Rules:
- Only
pendingandacceptedApplications are checked — existing confirmed Assignments are not cancelled (first-confirmed wins) - Overlap is determined by comparing
starts_at/ends_atof the shifts - The rejection reason is recorded as a system-generated
Taxonomy::GigStatusReason(time overlap)
Postconditions:
- Conflicting Applications are
rejected - Talent notified of each auto-rejection
UC-11: View applicants for a Shift
| Field | Details |
|---|---|
| Actor | Org::UserProfile (employer) |
| Trigger | Employer reviewing who applied |
Preconditions:
- Shift exists and has Applications
System Behavior:
- Employer views the applicant list for a Shift
- System displays Applications grouped by status:
pending: ranked bytalent_rank, showing score breakdownaccepted: awaiting talent confirmationconfirmed: double handshake complete, Assignment createdrejected/withdrawn/expired/declined: shown in a collapsed section for reference
- Each applicant shows: talent name, ranking score, category experience, reliability rating
Business Rules:
- Employer can accept or reject from this view
- Only
pendingapplicants can be accepted or rejected - The ranked list is the primary decision-making tool for employers
Postconditions:
- Read-only operation — actions (accept/reject) are separate UCs
Invariants
- One Application per talent per shift — a talent cannot apply twice to the same shift
- Applications are never hard-deleted
talent_rankandtalent_rank_detailare recalculated when the applicant pool changes- Only
pendingApplications can be accepted or rejected by the employer - Only
acceptedApplications can be confirmed or declined by the talent - The double handshake is: employer accepts (
pending→accepted) + talent confirms (accepted→confirmed). Both must happen before an Assignment is created. - Time overlap rejection only affects
pendingandacceptedApplications — confirmed Applications (with Assignments) are not auto-rejected rejected,withdrawn,expired,declined, andcancelledare terminal states- Rejection reasons are mandatory for employer-initiated rejections and system auto-rejections (time overlap)
- Application status reflects the outcome of the recruiting process — it does not track operational concerns (attendance, payment). Those belong to
Gig::Assignment.
Model Interactions
| Related Model | Relationship | Interaction |
|---|---|---|
Gig::Shift | Application belongs_to Shift | Talent applies to a specific shift. The shift's headcount determines how many can be selected. |
Talent::Profile | Application belongs_to TalentProfile | Identifies who applied. Talent's history feeds the ranking algorithm. |
Gig::Assignment | Application creates Assignment on confirmation | When both parties agree (status confirmed), the system creates an Assignment. The Application's job is then done. |
Taxonomy::GigStatusReason | Application references StatusReason | Required for employer rejections and system auto-rejections. Records why the application ended. |
Org::UserProfile | Employer accepts/rejects applications | Employers review the ranked list and make selection decisions. |
Schema Gaps
| Gap | Impact | Suggested Resolution |
|---|---|---|
Current DBML gig_applications uses is_employer_accept / is_talent_accept booleans | Doesn't capture the full status lifecycle (withdrawn, declined, expired) | Replace with status enum as designed in this spec |
No uuid on gig_applications | Inconsistent with other models | Add uuid string [unique, not null] |
No confirmed_at / accepted_at timestamps | Cannot track when each handshake step happened | Add accepted_at timestamp, confirmed_at timestamp |
No declined status in current DBML enum | Cannot distinguish "talent said no" from "talent didn't respond" | Add declined to the status enum |
No taxonomy_gig_status_reason_id on gig_applications | Cannot record why an application was rejected | Add taxonomy_gig_status_reason_id bigint [null] |
gig_shift_id typed as integer instead of bigint | Inconsistent with other FK columns | Change to bigint |
Ref type on gig_shift_id is < instead of > | Incorrect relationship direction in DBML | Change to ref: > gig_shifts.id |