Skip to content

Conversation

@mryhmln
Copy link
Collaborator

@mryhmln mryhmln commented Dec 19, 2025

No description provided.

… persons

Move ~25 person-specific data sections from top-level Application into
ApplicationPerson schema. Previously, these sections referenced household
members by name strings (e.g., personName: "Bianca Rivera"). Now,
person-specific data lives directly within each person in the persons array.

Changes:

- Created 31 new ancillary schemas at bottom of application.yaml:
  AdditionalExpense, DisabilityInfo, ExpectedIncomeChangeInfo,
  FederalHealthBenefit, FinancialResource, FosterCareHistory,
  HealthCoverageDetail, ImmigrationInfo, IncomeChangeReason, InsurancePolicy,
  Job, JobChange, LumpSumPayment, MedicareInfo, MortgageExpense,
  PersonExpenses, PregnancyInfo, PriorConvictionsInfo, Property, RentExpense,
  RetroactiveMedicalRequest, SelfEmploymentRecord, SocialSecurityApplication,
  StrikeInfo, StudentInfo, TaxFilingInfo, TransferredAsset, TribalInfo,
  UnearnedIncomeSource, UtilityExpense, Vehicle

- Added 30+ new fields to ApplicationPerson:
  jobs, selfEmployment, jobChanges, unearnedIncomeSources, lumpSumPayments,
  strikeInfo, financialResources, vehicles, insurancePolicies, properties,
  transferredAssets, disabilityInfo, socialSecurityApplications,
  healthCoverage, medicare, federalHealthBenefits, immigrationInfo,
  pregnancyInfo, fosterCareHistory, taxFilingInfo, tribalInfo,
  priorConvictions, expenses, expectedIncomeChange, incomeChangeReason,
  retroactiveMedicalRequest, studentInfo, hasFinancialAid,
  isMilitaryServiceMember, wantsFamilyPlanningBenefits, hasLegalClaim,
  separateMailAddress

- Simplified Application-level sections:
  Removed person-specific arrays, keeping only boolean flags like
  hasEmployment, hasDisability, isAnyonePregnant for quick filtering

- Kept at Application level (unchanged):
  dependentChildren.absentParents (external parties)
  householdDetails.roomersOrBoarders (non-household members)
  fosterCare.formerFosterCareMedicalAssistance (application-level tracking)
  healthInsurance.employerSponsoredCoverage (household-level)
  Household expense settings (utilities, housing assistance type)

- Updated examples in applications.yaml to use new structure
… persons

Move ~25 person-specific data sections from top-level Application into
ApplicationPerson schema. Previously, these sections referenced household
members by name strings (e.g., personName: "Bianca Rivera"). Now,
person-specific data lives directly within each person in the persons array.

Changes:

- Created 31 new ancillary schemas at bottom of application.yaml:
  AdditionalExpense, DisabilityInfo, ExpectedIncomeChangeInfo,
  FederalHealthBenefit, FinancialResource, FosterCareHistory,
  HealthCoverageDetail, ImmigrationInfo, IncomeChangeReason, InsurancePolicy,
  Job, JobChange, LumpSumPayment, MedicareInfo, MortgageExpense,
  PersonExpenses, PregnancyInfo, PriorConvictionsInfo, Property, RentExpense,
  RetroactiveMedicalRequest, SelfEmploymentRecord, SocialSecurityApplication,
  StrikeInfo, StudentInfo, TaxFilingInfo, TransferredAsset, TribalInfo,
  UnearnedIncomeSource, UtilityExpense, Vehicle

- Added 30+ new fields to ApplicationPerson:
  jobs, selfEmployment, jobChanges, unearnedIncomeSources, lumpSumPayments,
  strikeInfo, financialResources, vehicles, insurancePolicies, properties,
  transferredAssets, disabilityInfo, socialSecurityApplications,
  healthCoverage, medicare, federalHealthBenefits, immigrationInfo,
  pregnancyInfo, fosterCareHistory, taxFilingInfo, tribalInfo,
  priorConvictions, expenses, expectedIncomeChange, incomeChangeReason,
  retroactiveMedicalRequest, studentInfo, hasFinancialAid,
  isMilitaryServiceMember, wantsFamilyPlanningBenefits, hasLegalClaim,
  separateMailAddress

- Simplified Application-level sections:
  Removed person-specific arrays, keeping only boolean flags like
  hasEmployment, hasDisability, isAnyonePregnant for quick filtering

- Kept at Application level (unchanged):
  dependentChildren.absentParents (external parties)
  householdDetails.roomersOrBoarders (non-household members)
  fosterCare.formerFosterCareMedicalAssistance (application-level tracking)
  healthInsurance.employerSponsoredCoverage (household-level)
  Household expense settings (utilities, housing assistance type)

- Updated examples in applications.yaml to use new structure
@pkarman
Copy link

pkarman commented Jan 5, 2026

A positive direction for change.

Notes below grouped by top-level schema name:

Application

  • primaryApplicant seems like mostly a proxy into the "persons" array. Why are these attributes here rather than on the ApplicationPerson record itself?

  • primaryApplicant.speaksEnglish and .preferredLanguage seem misaligned with the questionnaire, which asks about preferred written language and preferred spoken language

  • signatures.spouseCoApplicationSignature - why "spouse"? Why not just "coApplicantSignature"?

  • personWhoHelpedCompleteApplication - why not in "persons" array?

  • householdDetails.roomersOrBoarders - why "name" attribute instead of PersonId into "persons" array?

  • householdDetails.institutionalizedMembers - why "name" attribute instead of PersonId into "persons" array?

  • expeditedSNAPDetails.utilityCosts - why separate object for monthly costs of utilities? they do not have any attributes beyond what .monthlyMortgage and .monthlyRent offer

  • expeditedSNAPDetails.receivedBenefitsOtherState - simple boolean is insufficient. This is member-specific and requires other attributes (receipt date, city, state, county) per-member.

  • voterRegistration - why object with a single boolean attribute, instead of just a boolean?

  • dependentChildren.absentParents - why "name" attribute instead of PersonId into "persons" array? Why is .forWhichChild a string instead of a PersonId into "persons" array?

  • dependentChildren.wantsGoodCauseFromChildSupport - misnamed and confusing wording in the description. We need the word "waiver" or "exception" to indicate what the "good cause" relates to. The field indicates that the applicant wants to waive child support services for a good cause (fear of violence or contact, e.g.)

  • fosterCare.formerFosterCareMedicalAssistance - why "name" attribute and not PersonId?

  • nonCitizen.emergencyMedicaidApplicants - why "name" array and not PersonIds?

  • earnedIncome, unearnedIncome, expenses, students - these seem like application-level boolean flags or total sums of Person-level data that are duplicative of what is stored at the Person level. Is the idea convenience? Otherwise, it seems redundant.

  • most of the remaining Application-level fields are "roll up" flags of data stored at the Person level. Are these necessary?

ApplicationPerson

  • gender - rename to "sex"

  • isUSCitizen - redundant with "citizenshipStatus"

  • programsApplyingFor - let's make this an array of strings rather than object. There's no way to enforce mutually exclusive attributes on an object (e.g. can check both "notApplying" and "snap").

  • separateMailAddress - seems misnamed, and the description seems artificially limited to "medical correspondence". Can it just be "mailingAddress" and leave it at that?

Programs

  • does this need to be a separate object? It's not clear to me why an application is not simply associated with an array of enum (string) values, or why "cashPrograms" are further separated into sub programs. Having an array of strings makes the schema more general-purpose as well (i.e. some states may offer more programs that they ones enumerated here).

ImmigrationInfo

  • sponsors could be people (they have names, address, SSN). Why not separate "ApplicationPerson" from "ApplicationMember" where ApplicationMember extends ApplicationPerson?

@mryhmln mryhmln changed the base branch from multi-state-support to main January 6, 2026 14:54
@mryhmln
Copy link
Collaborator Author

mryhmln commented Jan 6, 2026

Application

primaryApplicant seems like mostly a proxy into the "persons" array. Why are these attributes here rather than on the ApplicationPerson record itself? Because none of the other ApplicationPersons need to support those properties. We could define another schema, e.g. ApplicationPersonPrimary or ApplicationPersonApplicant and have it inherit from ApplicationPerson so that the persons array supports it. Would you prefer that approach?

primaryApplicant.speaksEnglish and .preferredLanguage seem misaligned with the questionnaire, which asks about preferred written language and preferred spoken language The PDF application has a question "Do you speak and read English? with options of yes, no, and if no, what language do you speak? It doesn't separate out spoken and written. Do we want to?

signatures.spouseCoApplicationSignature - why "spouse"? Why not just "coApplicantSignature"? No specific reason. Just a literal translation from "Spouse's/Co-Applicant's signature" in the application. I will rename it.

personWhoHelpedCompleteApplication - why not in "persons" array? **Because that person isn't a member of the household. The persons array is meant to represent household members. We can refactor ApplicationPerson to have the minimum properties to support all of the different types of people and then use inheritance for the different people types if you'd prefer. I still don't think I would add people to that array who aren't household members though. And I might actually rename persons to household to make that clear. **

householdDetails.roomersOrBoarders - why "name" attribute instead of PersonId into "persons" array? Because not all persons will have Id's, as they won't be stored separately in the system, unlike the primary applicant. Also, at the time of filling out the application, Id values won't be known.

householdDetails.institutionalizedMembers - why "name" attribute instead of PersonId into "persons" array? Same rationale as for householdDetails.roomersOrBoarders

expeditedSNAPDetails.utilityCosts - why separate object for monthly costs of utilities? they do not have any attributes beyond what .monthlyMortgage and .monthlyRent offer Because the PDF application has a question "Do you have any of these utilities? If so, cost per month?, with options for electricity, water, phone, trash, sewer, and other, with a place to provide amounts.

expeditedSNAPDetails.receivedBenefitsOtherState - simple boolean is insufficient. This is member-specific and requires other attributes (receipt date, city, state, county) per-member. You're right. This looks wrong. Will add these attributes as well.

voterRegistration - why object with a single boolean attribute, instead of just a boolean? No reason. Will change.

dependentChildren.absentParents - why "name" attribute instead of PersonId into "persons" array? Why is .forWhichChild a string instead of a PersonId into "persons" array? Same rationale as for householdDetails.roomersOrBoarders

dependentChildren.wantsGoodCauseFromChildSupport - misnamed and confusing wording in the description. We need the word "waiver" or "exception" to indicate what the "good cause" relates to. The field indicates that the applicant wants to waive child support services for a good cause (fear of violence or contact, e.g.) **What about dependentChildren.familyViolenceOptionWaiver? **

fosterCare.formerFosterCareMedicalAssistance - why "name" attribute and not PersonId? Same rationale as for householdDetails.roomersOrBoarders

nonCitizen.emergencyMedicaidApplicants - why "name" array and not PersonIds? Same rationale as for householdDetails.roomersOrBoarders

earnedIncome, unearnedIncome, expenses, students - these seem like application-level boolean flags or total sums of Person-level data that are duplicative of what is stored at the Person level. Is the idea convenience? Otherwise, it seems redundant. Those were meant to leave room for attributes specified at the household vs. person level, e.g. expenses and student properties like grantsScholarshipsWorkStudyForLivingExpenses, although for student info, the application isn't really that clear about whether those are household level or person level, since it doesn't really provide a place to support multiple students easily.

most of the remaining Application-level fields are "roll up" flags of data stored at the Person level. Are these necessary? No. Unless we want to support easy ruling out whether or not any people in the household meet specific criteria without having to loop through every person in the list.

ApplicationPerson

gender - rename to "sex" ok

isUSCitizen - redundant with "citizenshipStatus" true. will remove citizenshipStatus.

programsApplyingFor - let's make this an array of strings rather than object. There's no way to enforce mutually exclusive attributes on an object (e.g. can check both "notApplying" and "snap"). ok

separateMailAddress - seems misnamed, and the description seems artificially limited to "medical correspondence". Can it just be "mailingAddress" and leave it at that? sure

Programs

does this need to be a separate object? It's not clear to me why an application is not simply associated with an array of enum (string) values, or why "cashPrograms" are further separated into sub programs. Having an array of strings makes the schema more general-purpose as well (i.e. some states may offer more programs that they ones enumerated here). That makes more sense. Will change that.

ImmigrationInfo

sponsors could be people (they have names, address, SSN). Why not separate "ApplicationPerson" from "ApplicationMember" where ApplicationMember extends ApplicationPerson? See comments re: refactoring ApplicationPerson. Assuming ApplicationPerson would be the "base class" with minimal properties, which attributes do you think make sense aside from name and relationship, assuming people could be non-household individuals like someone who helps complete application, sponsor, etc.?

@pkarman
Copy link

pkarman commented Jan 6, 2026

Because not all persons will have Id's, as they won't be stored separately in the system, unlike the primary applicant. Also, at the time of filling out the application, Id values won't be known.

ok this is a good answer because it reveals some differing assumptions we both have.

I am assuming that every household member will be stored in the backend system, because benefit determination is made per household member, not just for the primary applicant. Not all household members may be applying for benefits, and each member may be applying for different programs than other members.

E.g., a single parent with 2 children in the home, one with special needs, and a renter/boarder. The parent is the primary applicant, both children apply for SNAP, and one for Medicaid. There are 4 household members, but only 2 are applying for benefits and neither applying is the primary applicant.

Re: IDs, unless the FE is assigning IDs, no IDs are available on initial POST submit, but subsequent GET requests and/or PATCH/PUT requests should have the IDs available, since backend will assign them. Backend may also assign state IDs for identity resolution.

So in this example above, both children will get IDs in the system, as well as the primary applicant, and the boarder/renter will as well, since they are considered a household member (they affect the household's finances directly).

@pkarman
Copy link

pkarman commented Jan 6, 2026

The PDF application has a question "Do you speak and read English? with options of yes, no, and if no, what language do you speak? It doesn't separate out spoken and written. Do we want to?

yes, I think so. It provides parity and accurately reflects the separate needs for interviewers (who may need a translator present) and written correspondence.

@pkarman
Copy link

pkarman commented Jan 6, 2026

Assuming ApplicationPerson would be the "base class" with minimal properties, which attributes do you think make sense aside from name and relationship, assuming people could be non-household individuals like someone who helps complete application, sponsor, etc.?

  • first name
  • last name
  • middle name
  • name suffix
  • dob
  • sex
  • SSN

@pkarman
Copy link

pkarman commented Jan 6, 2026

Unless we want to support easy ruling out whether or not any people in the household meet specific criteria without having to loop through every person in the list.

An affordance to make the UI logic simpler does seem reasonable. Maybe it should be clear though that those fields are populated on GET requests but are ignored by the BE on POST/PUT/PATCH requests?

@mryhmln
Copy link
Collaborator Author

mryhmln commented Jan 6, 2026

Because not all persons will have Id's, as they won't be stored separately in the system, unlike the primary applicant. Also, at the time of filling out the application, Id values won't be known.

ok this is a good answer because it reveals some differing assumptions we both have.

I am assuming that every household member will be stored in the backend system, because benefit determination is made per household member, not just for the primary applicant. Not all household members may be applying for benefits, and each member may be applying for different programs than other members.

E.g., a single parent with 2 children in the home, one with special needs, and a renter/boarder. The parent is the primary applicant, both children apply for SNAP, and one for Medicaid. There are 4 household members, but only 2 are applying for benefits and neither applying is the primary applicant.

Re: IDs, unless the FE is assigning IDs, no IDs are available on initial POST submit, but subsequent GET requests and/or PATCH/PUT requests should have the IDs available, since backend will assign them. Backend may also assign state IDs for identity resolution.

So in this example above, both children will get IDs in the system, as well as the primary applicant, and the boarder/renter will as well, since they are considered a household member (they affect the household's finances directly).

This is helpful info. A couple follow up questions then...

  1. should we assume that sponsors and people who assist in filling out an application are not household members? Or can they be both?
  2. Assuming all household members will be stored in the backend system, I would expect them to have not only an id and some minimal properties, but also a relationship to one or more applications. Is that a fair assumption? And in that case, which properties would you expect to be associated with a household person aside from name, dob, sex, and ssn?

@pkarman
Copy link

pkarman commented Jan 6, 2026

should we assume that sponsors and people who assist in filling out an application are not household members? Or can they be both?

They can be both.

Assuming all household members will be stored in the backend system, I would expect them to have not only an id and some minimal properties, but also a relationship to one or more applications. Is that a fair assumption?

yes

And in that case, which properties would you expect to be associated with a household person aside from name, dob, sex, and ssn?

a household member would have properties like:

  • preferred spoken language
  • preferred written language
  • ethnicity
  • race
  • citizenship status
  • marital status

and the dozens of other properties you currently have on the ApplicationPerson schema in this PR.

@mryhmln
Copy link
Collaborator Author

mryhmln commented Jan 7, 2026

and the dozens of other properties you currently have on the ApplicationPerson schema in this PR.

I'm thinking this is also worth clarifying further. My intention for delineating Person from ApplicationPerson was to store information about the person used for eligibility determination purposes (and which could change over time) with the application itself and linking the person to the application so that it could be referenced later as a snapshot in time, i.e. this is the information the person provided at this point in time that was used to determine eligibility for these programs. That would also allow us to save history of this information. I was thinking Person would contain properties that shouldn't change over time, i.e. identifying information. Are you good with that approach? And if so, what properties would you want to store with Person proper?

… data

Person now contains only persistent identity and demographic information.
HouseholdMember (renamed from ApplicationPerson) contains eligibility-specific
fields for application processing, extending Person's schemas via allOf.

Information is now organized by domain - each domain container (health, tribal,
citizenship, contact, employment, etc.) has a base version in Person with
persistent fields, and an extended version in HouseholdMember with eligibility
fields added. Screening flags provide a quick summary of what applies to an
application without traversing nested data, while preferences capture applicant
choices separately from the application data itself.

This domain-based architecture better supports state-specific variations by
making it easier to change or replace individual pieces or entire sections
via overlays.

Updated all examples and documentation. All tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants