Skip to content

OSCER-496: Enforce CE determination_data with value objects#521

Merged
jgavin-nava merged 14 commits into
mainfrom
jgavin/OSCER-496-determination-data-value-objects
May 21, 2026
Merged

OSCER-496: Enforce CE determination_data with value objects#521
jgavin-nava merged 14 commits into
mainfrom
jgavin/OSCER-496-determination-data-value-objects

Conversation

@jgavin-nava

@jgavin-nava jgavin-nava commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Ticket

Resolves OSCER-496

Changes

  • Introduced Determinations::HoursBasedDeterminationData, IncomeBasedDeterminationData, and ExternalCECombinedDeterminationData (ValueObject) as the canonical, validated serialization layer for automated CE determination_data written by CertificationCase (hours, income, and combined hours + income paths).
  • Replaced ad-hoc hash builders on CertificationCase with VO #to_h output for those flows; manual activity report accept/deny now use the hours VO as well.
  • Documented on Determination and in docs/architecture/income-data/income-data.md that the value objects are the contract; other flows (exemption placeholder, eligibility JSON) remain out of scope for the VOs.
  • Breaking (persisted JSON + public API): combined CE determination_data["calculation_type"] is now external_ce_combined (Determination::CALCULATION_TYPE_EXTERNAL_CE_COMBINED). Historical rows may still have ex_parte_ce_combined — use Determination::CALCULATION_TYPE_EXTERNAL_CE_COMBINED_LEGACY in SQL/BI/read paths that must match both. CertificationCase#record_ex_parte_ce_combined_assessment was renamed to record_external_ce_combined_assessment; any external callers or docs must follow the new name.
  • ExternalCECombinedDeterminationData.build eagerly validates nested hour and income aggregates (not only on #to_h) so invalid payloads fail before persist.
  • Contract specs under spec/models/determinations/ (hours_based_determination_data_spec.rb, income_based_determination_data_spec.rb, external_ce_combined_determination_data_spec.rb) assert stable serialized shapes for hours_based, income_based, and external_ce_combined, plus legacy constant and build-time validation coverage.

Context for reviewers

  • Zeitwerk: combined VO file is external_ce_combined_determination_data.rbDeterminations::ExternalCECombinedDeterminationData (project CE acronym inflection).
  • No DB migration: legacy JSON values remain in existing rows; new writes emit external_ce_combined.
  • PR description intentionally calls out BI / SQL / integrations that filtered on ex_parte_ce_combined or called the old case method.

Testing

From reporting-app/:

  • make lint-ci
  • make test
    (If the Docker image lacks gems, run docker compose run --rm reporting-app sh -c "bundle install && bundle exec rspec …" first, per local setup.)

Targeted run used during development:

  • bundle exec rspec spec/models/determinations/hours_based_determination_data_spec.rb spec/models/determinations/income_based_determination_data_spec.rb spec/models/determinations/external_ce_combined_determination_data_spec.rb spec/models/certification_case_spec.rb spec/services/community_engagement_check_service_spec.rb spec/services/hours_compliance_determination_service_spec.rb spec/services/income_compliance_determination_service_spec.rb

Full suite before push: make test — 1876 examples, 0 failures, 1 pending (pre-existing).

Preview environment for reporting-app

♻️ Environment destroyed ♻️

- Add Determinations::{HoursBased,IncomeBased,ExParteCECombined}DeterminationData
  (ValueObject) for validated serialization to determination_data JSONB
- CertificationCase automated CE write path uses VOs; remove ad-hoc hash builders
- Document canonical contract vs legacy rows on Determination and in income-data.md
- Add determinations_ce_determination_data_contract_spec for stable shapes per calculation_type

Made-with: Cursor
- Determination::CALCULATION_TYPE_CE_COMBINED = ce_combined; doc legacy ex_parte_ce_combined rows
- Determinations::CECombinedDeterminationData + ce_combined_determination_data.rb
- CertificationCase#record_ce_combined_assessment, #ce_combined_reason_codes
- Update CommunityEngagementCheckService, specs, income-data.md

Made-with: Cursor
…dation

- Add Determination::CALCULATION_TYPE_CE_COMBINED_LEGACY; reference in comments and income-data.md
- Reword Determination and HoursBasedDeterminationData comments (combined CE wording)
- CECombinedDeterminationData.build validates nested VOs eagerly; YARD @raise
- Spec: legacy constant + build fails on invalid nested hours aggregate
- PR body draft: nava-notes/PRs/PR-OSCER-496-description.md (vault, not in repo)

Made-with: Cursor
@jgavin-nava jgavin-nava requested a review from a team as a code owner April 30, 2026 16:39
@jgavin-nava jgavin-nava requested a review from giverm April 30, 2026 16:39
jgavin-nava and others added 2 commits April 30, 2026 16:27
- CECombinedDeterminationData: assign nested VOs in validate_nested; reuse in to_h via accessors
- HoursBasedDeterminationData: YARD on class and #to_h for service-controlled numeric aggregates

Made-with: Cursor
- hours_based_determination_data_spec, income_based, ce_combined (RSpec path per class)
- Move CALCULATION_TYPE_CE_COMBINED_LEGACY example to determination_spec
- Update income-data.md and remove determinations_ce_determination_data_contract_spec

Co-authored-by: Cursor <cursoragent@cursor.com>

@jdettmannnava jdettmannnava left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just some tweak options and test opportunities.

Comment thread reporting-app/app/models/determinations/income_based_determination_data.rb Outdated
Comment thread reporting-app/app/models/certification_case.rb
Comment thread reporting-app/app/models/determinations/income_based_determination_data.rb Outdated
jgavin-nava and others added 6 commits May 1, 2026 12:28
…come_by_source

Co-authored-by: Cursor <cursoragent@cursor.com>
…nationData#to_h

Pair by_category with by_source for symmetric readability (PR review).

Co-authored-by: Cursor <cursoragent@cursor.com>
- CE combined: hours-only and income-only satisfied_by; assert nested
  compliant on both track pass; rename stable payload example
- Hours/income: compliant key when nested; omit when standalone CE

Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve determination/CE conflicts with main: record_external_ce_combined_assessment,
CALCULATION_TYPE_EXTERNAL_CE_COMBINED + _LEGACY, ExternalCECombinedDeterminationData VO;
hours/income VOs use external_hourly_activity_ids, external_income_activity_ids, and
income_by_source external/activity keys matching aggregate services.

Co-authored-by: Cursor <cursoragent@cursor.com>
- HoursBasedDeterminationData: apply with_indifferent_access in from_aggregate; document string/symbol keys
- IncomeBasedDeterminationData: same; ALLOWED_INCOME_BY_SOURCE_KEYS + validation for unknown keys
- ExternalCECombinedDeterminationData: store indifferent-access copies in .build; YARD @raise ArgumentError on #to_h without .build
- Specs: leaf negative/string-key cases, hours_by_category non-Hash, combined VO new/to_h without .build

Co-authored-by: Cursor <cursoragent@cursor.com>
CALCULATION_TYPE_EXTERNAL_CE_COMBINED = "external_ce_combined"
# Stored in +determination_data["satisfied_by"]+ when +calculation_type+ is +CALCULATION_TYPE_EXTERNAL_CE_COMBINED+.
# Historical +determination_data["calculation_type"]+ before +external_ce_combined+; use for BI/read filters on old rows.
CALCULATION_TYPE_EXTERNAL_CE_COMBINED_LEGACY = "ex_parte_ce_combined"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if it is better to leave so it is known but I dont think any real data would have this information or add in a migration to make sure to update any rows to the right type when needed? Or just remove it in general as I stated i dont think any real data would have this type?

@jgavin-nava jgavin-nava requested a review from jdettmannnava May 21, 2026 15:19
Comment thread reporting-app/spec/models/determinations/hours_based_determination_data_spec.rb Outdated
Comment thread reporting-app/spec/models/determinations/income_based_determination_data_spec.rb Outdated
Share one expected_calculated_at let with travel_to so frozen-time
expectations stay aligned across the three value-object spec files.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jgavin-nava jgavin-nava merged commit ee00e49 into main May 21, 2026
12 checks passed
@jgavin-nava jgavin-nava deleted the jgavin/OSCER-496-determination-data-value-objects branch May 21, 2026 17:20
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.

Enforce Determination.determination_data with value object(s)

2 participants