Skip to content

fix(hr): backport the one-row-per-person class_people fix to release-2026.07.1 - #2509

Merged
aleksdotbar merged 2 commits into
release-2026.07.1from
backport/class-people-grain-2026.07.1
Aug 13, 2026
Merged

fix(hr): backport the one-row-per-person class_people fix to release-2026.07.1#2509
aleksdotbar merged 2 commits into
release-2026.07.1from
backport/class-people-grain-2026.07.1

Conversation

@aleksdotbar

Copy link
Copy Markdown
Contributor

Backport of #2125 (3062d4f2 + 83db3abf, merged to main 2026-08-03; the release branch predates it).

Why

The release branch still has both defects #2125 fixed, and together they create duplicate current rows in silver.class_people:

  1. __to_class_people staging views read append-only bronze without FINAL, so unmerged Airbyte snapshot rows leak through as extra rows.
  2. unique_key embeds a per-version axis (lastChanged for BambooHR), turning each transient duplicate into a permanently distinct silver key that neither RMT nor FINAL can collapse.

On an environment running this branch, class_people FINAL can hold more rows than there are accounts, which overstates headcount and duplicates people downstream of the person registry.

What

Clean cherry-pick of the two PR #2125 commits, covering all four HR connectors (bamboohr, active-directory, ms-entra, workday): entity-level unique_key, deterministic FINAL reads, the assert_class_people_one_row_per_person dbt test, and the matching DDL/docs updates. One conflict in the BambooHR schema.yml model descriptions resolved in favor of this branch's wording (it already carries the full-field-sync descriptions).

dbt parse clean.

Validation

After deploy, SELECT count() FROM silver.class_people FINAL grouped by source should return exactly one row per account; the new dbt test enforces this in CI.

Roman Mitasov and others added 2 commits August 13, 2026 18:53
`silver.class_people` overstated headcount by ~5% on virtuozzo (1440 rows for
1420 employees; 414 "active" rows for 394 active people). Two defects
compounded — neither is sufficient alone:

1. The `__to_class_people` staging views read append-only bronze WITHOUT
   `FINAL`. Airbyte appends a full snapshot per sync and bronze is
   RMT(_airbyte_extracted_at), which only collapses on background merge, so a
   bare read emits every unmerged snapshot row.
2. Those views appended a version axis (`lastChanged` / `createdDateTime` /
   `whenCreated` / `Last_Functionally_Updated`) to `unique_key`, per ADR-0004.
   That turned each transient bronze duplicate into a permanently distinct
   silver key, so the versionless RMT could never collapse it — and `FINAL`
   could not either, because the keys genuinely differ. `valid_to` was
   hardcoded NULL, so nothing marked which row was current.

The surplus was regenerated on every run and drifted with bronze merge timing,
which is why it surfaced as two separate audit findings ("duplicate active
emails" and "active-count drift").

class_people is a current-state snapshot, not an SCD2 history table: it is
`materialized='table'` and rebuilt in full each run, so it cannot accumulate
history by construction. HR attribute history already lives in the per-source
`*_snapshot` / `*_fields_history` chain, which is incremental+append and tracks
strictly more fields.

Changes:
* All four `__to_class_people` views (active-directory, bamboohr, ms-entra,
  workday): entity-level `unique_key`, `valid_to` dropped, bronze read with
  `FINAL`. Projections stay aligned 1:1 for the positional UNION.
* `ELSE 'active'` catch-alls replaced with `'unknown'`. A BambooHR record with
  `status=''` / `employmentHistoryStatus='Third party'` was being counted as
  active — this is the residual +1 between bronze (393) and silver (394).
  `accepted_values` updated accordingly.
* Same missing `FINAL` fixed in `bamboohr__working_hours` /
  `workday__working_hours`: there the `status='Active'` filter ran before
  dedup, so a leaver could still qualify via a stale snapshot row.
* New `assert_class_people_one_row_per_person` data-quality check, plus
  `unique`/`not_null` on `class_people.unique_key`.
* Reconcile the contract, which contradicted itself: ADR-0004 mandated the
  version axis while DESIGN.md and ADR-0001 called class_people a pure
  snapshot, and ADR-0001 exempted it from the read-dedup cleanup on the
  now-false premise that it "already collapses to one row per key".

Verified against the live virtuozzo cluster: the fixed transform yields
1420 rows / 1420 distinct keys, active 394 -> 393, unknown 1; the new check
reports exactly the 20 current violations and goes green after a rebuild.
No migration needed — `materialized='table'` fully replaces the table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
(cherry picked from commit 3062d4f)
The committed DDL snapshot still declared `valid_to Nullable(DateTime)` on
`silver.class_people`. The `connectors-ddl snapshot + field parity` job
rebuilds the database from the dbt models and re-dumps the DDL, so removing
the column from the four `__to_class_people` producers made the snapshot
drift and fail the gate.

This is exactly the one-line delta the CI drift check computed; no other
snapshot references `valid_to`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
(cherry picked from commit 83db3ab)
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fe5c3958-4f10-48da-b174-d406724db1b0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@aleksdotbar
aleksdotbar merged commit fcbb420 into release-2026.07.1 Aug 13, 2026
24 checks passed
@aleksdotbar
aleksdotbar deleted the backport/class-people-grain-2026.07.1 branch August 13, 2026 11:00
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.

2 participants