Skip to content

Profile data gaps (technical)

What IntakeProfile needs beyond its original onboarding shape to answer every question in the taxonomy. All additions live on the new, optional IntakeProfile.Extended (ExtendedProfile) object in IntakeProfile.cs — nothing existing was renamed, removed, or repurposed, so profiles saved before this change deserialize unchanged (System.Text.Json defaults an absent nested object to its parameterless constructor).

Plain-language version: What WorkWingman Still Needs to Ask You. For the user-facing framing of why these questions get asked at all, see ../plain/application-question-taxonomy.md.

Why a nested Extended object instead of flattening onto IntakeProfile

  • Backward compatibility is automatic. Adding new top-level properties to IntakeProfile would also be backward-compatible with System.Text.Json, but grouping the additions keeps the diff against the original onboarding shape self-contained and makes it obvious, on review, which fields are "core onboarding" (always asked) vs. "gap-fill" (asked opportunistically, filled in over time as forms demand them).
  • LocalJsonStore (see LocalJsonStore.cs) round-trips whatever shape IntakeProfile has via System.Text.Json — no migration script is needed either way, but the grouping makes it easy to eventually version/migrate Extended independently if it grows large enough to warrant its own store document.

Gaps and the proposed fields

Gap New field(s) on ExtendedProfile Why the original profile didn't have it
References References: List<ReferenceContact> (name, relationship, company, phone, email) Onboarding focused on the applicant's own history; references are a distinct, sensitive category (another person's contact info) worth its own explicit consent step.
Salary history SalaryHistory: string Increasingly illegal to ask (many US states/cities ban salary-history questions) — kept separate from Eligibility.SalaryExpectation (which is your ask, always fair game) so the resolver can treat them differently and so the app can eventually warn "this job's state doesn't allow this question."
Willingness to relocate WillingToRelocate: bool? Eligibility.RelocationPreference already exists but is free text (e.g. "Open to Austin or Denver") — good for cover letters, bad for a strict form yes/no. The new field is a clean tri-state (yes/no/not set) AnswerResolver can map directly, falling back to the free text when the structured value isn't set.
Certifications / licenses Certifications: List<CertificationEntry> (name, issuing body, issued/expires dates, credential ID) Original profile only had TopSkills (flat strings) — certifications need structure (an expiring AWS cert is different from a skill keyword) and some forms verify the credential ID directly.
Security clearance level SecurityClearanceLevel: string, SecurityClearanceExpiresOn: DateOnly? Eligibility.SecurityClearance already exists as a free-text summary; the structured level + expiration lets AnswerResolver answer "do you hold an active clearance" precisely (a lapsed Secret clearance often answers differently than an active one) rather than string-matching free text.
Availability / notice period NoticePeriod: string Distinct from Eligibility.EarliestStartDate (a date) — notice period is a duration owed to the current employer ("2 weeks", "1 month"), which some forms ask for directly instead of/alongside a start date.
"How did you hear about us?" HowDidYouHearAboutUs: string Nearly universal custom question with no prior home; defaults to one source (e.g. "LinkedIn") reusable across applications unless a specific job warrants a referral callout.
Rehire eligibility PreviouslyEmployedByCompany: bool? Common on internal-transfer-aware ATSes (Workday especially) — "have you worked here before" routes to a different onboarding flow than a fresh applicant.
Non-compete / non-solicit BoundByNonCompete: bool?, NonCompeteDetails: string Increasingly asked as companies get more cautious about hiring from direct competitors; a knockout-adjacent question the original profile had no home for.
Age attestation IsAtLeast18: bool (defaults true) Asked on essentially every application; better to collect once at onboarding than re-derive it, and better to make it explicit than to assume.
Disclosable conviction HasDisclosableConviction: bool?, ConvictionDetails: string The canonical "yes/no → N/A conditional text box" example (see taxonomy → edge cases); needs both the trigger answer and, if applicable, real explanatory text, worded consistently across applications.
Portfolio links PortfolioLinks: List<string> GitHub/personal site/publications beyond the resume file itself — ConnectionsService already tracks a GitHub connection, but the profile had no field to place the actual link(s) on an application.
Desired title DesiredTitle: string Some forms ask "what role are you interested in" as a free-standing question distinct from the job being applied to (e.g. general-application forms, talent-community signups).
Employment type preference EmploymentTypePreference: string W-2 vs. 1099/contract willingness — some ATSes (especially staffing-agency-fronted ones) ask this directly.

What this does not attempt to solve yet

  • Per-skill years of experience (YearsOfExperienceInSkill in the taxonomy) needs a structured "skill → years" map, not just the flat TopSkills list. Deferred because the UX for capturing it (a full skills matrix at onboarding) is a bigger product decision than this pass scopes; today it resolves as a gap routed to a judgement call, same as WorkdayAutomationEngine's existing low-confidence-field flow.
  • Reason for leaving, per employer — needs per-WorkHistoryEntry structure (a Reason field on WorkHistoryEntry itself) rather than a single profile-wide value, since the answer differs per job. Left out of this pass to avoid scope creep on the work-history shape; noted here so it's not lost.
  • Per-state salary-history legalitySalaryHistory is collected, but there's no logic yet that suppresses the question where asking is illegal. That's an ATS-detection-time decision (which state/city the job is in), not a profile-shape decision, and belongs closer to AtsDetector/WorkdayAutomationEngine once that jurisdiction data source is chosen.