Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
dfbb22e
feat: add compensation change review quality gate
seonghobae Aug 19, 2026
7486d44
docs: add governed compensation change ADR
seonghobae Aug 19, 2026
c6e19da
docs: record compensation review references
seonghobae Aug 19, 2026
c29f3cb
docs: trace compensation change review
seonghobae Aug 19, 2026
519236e
docs: add compensation review changelog
seonghobae Aug 19, 2026
6cf6587
docs: explain compensation change review
seonghobae Aug 19, 2026
cf170a0
feat: add compensation review package metadata
seonghobae Aug 19, 2026
2611ea8
feat: export compensation review contract
seonghobae Aug 19, 2026
989eee3
feat: add governed compensation change review packet
seonghobae Aug 19, 2026
2b78f0d
test: cover compensation change review contract
seonghobae Aug 19, 2026
59f6ce0
Merge protected develop into compensation change review
seonghobae Aug 19, 2026
6eded6b
chore: reconcile compensation review onto protected develop
seonghobae Aug 19, 2026
ac439f3
chore: reconcile compensation review onto restored develop
seonghobae Aug 19, 2026
5ad430a
test: reject UUIDv1 compensation trust references
seonghobae Aug 20, 2026
f6eb5ef
fix: require UUIDv4 compensation trust references
seonghobae Aug 20, 2026
1820a25
docs: define UUIDv4 compensation reference privacy
seonghobae Aug 20, 2026
3242733
docs: record UUIDv4 compensation trust-reference decision
seonghobae Aug 20, 2026
9fa44c9
docs: record compensation reference privacy hardening
seonghobae Aug 20, 2026
a83a8b0
docs: trace UUIDv4 compensation reference regression
seonghobae Aug 20, 2026
f551fd3
test: reject UUIDv1 tenant identity in compensation review
seonghobae Aug 20, 2026
79ab792
fix: require opaque UUIDv4 tenant identity
seonghobae Aug 20, 2026
cf713cd
docs: bind compensation tenant identity to UUIDv4
seonghobae Aug 20, 2026
2437728
docs: record compensation tenant UUIDv4 privacy repair
seonghobae Aug 20, 2026
ccfcde5
docs: trace compensation tenant UUIDv4 regression
seonghobae Aug 20, 2026
e57369f
docs: make compensation tenant UUIDv4 part of privacy decision
seonghobae Aug 20, 2026
fcefb33
Merge remote-tracking branch 'refs/remotes/origin/develop' into HEAD
seonghobae Aug 20, 2026
73c778b
test: accept canonical HRIS tenant UUID contract
seonghobae Aug 20, 2026
3551dfe
fix: honor authoritative tenant UUID contract
seonghobae Aug 20, 2026
04ddc65
docs: align compensation tenant identity boundary
seonghobae Aug 20, 2026
1934a08
chore: integrate protected develop into compensation-change lane
seonghobae Aug 20, 2026
5a6c2f3
chore: reconcile compensation change review with current develop
seonghobae Aug 20, 2026
f6cefeb
chore(compensation): integrate protected develop after #41
seonghobae Aug 20, 2026
4d40a0d
chore(compensation): integrate protected develop after #43
seonghobae Aug 21, 2026
76db3c3
test(compensation-review): reject recorded-time subclasses
seonghobae Aug 21, 2026
18716ae
fix(compensation-review): require exact recorded-time type
seonghobae Aug 21, 2026
16fa40b
fix(compensation-review): align timestamp validation contract
seonghobae Aug 21, 2026
a3294b0
fix(compensation-review): restore packet and align timestamp message
seonghobae Aug 21, 2026
3826dcf
test(comp-review): reject forged string evidence types
seonghobae Aug 21, 2026
9901e99
fix(comp-review): require exact string evidence types
seonghobae Aug 21, 2026
684b439
test(comp-review): reject forged governance text
seonghobae Aug 21, 2026
e83f71c
fix(compensation-review): reject forged governance text
seonghobae Aug 21, 2026
29c9fca
test(compensation-review): expose post-issuance evidence rewrite
seonghobae Aug 24, 2026
736b70b
fix(compensation-review): bind emitted evidence to issuance
seonghobae Aug 24, 2026
0a956f7
test(compensation-review): exercise issuance cleanup
seonghobae Aug 24, 2026
4a3035c
docs(compensation-review): record issuance integrity repair
seonghobae Aug 24, 2026
e2bcb85
docs(compensation-review): explain creation-bound evidence
seonghobae Aug 24, 2026
c9af575
docs(compensation-review): trace post-issuance integrity
seonghobae Aug 24, 2026
dc3fa7e
fix(compensation-review): reject digest string subclasses
seonghobae Aug 28, 2026
b1f1aa5
test(compensation-review): remove invalid static f-string
seonghobae Aug 28, 2026
28e26a8
test(compensation-review): require one canonical valid packet fixture
seonghobae Aug 28, 2026
551a83e
test(compensation-review): add canonical valid packet fixture
seonghobae Aug 28, 2026
0248764
test(compensation-review): use shared valid packet fixture
seonghobae Aug 28, 2026
ec47ec3
test(compensation-review): reuse shared fixture for issuance integrity
seonghobae Aug 28, 2026
da15881
test(compensation-review): reuse shared fixture for reference privacy
seonghobae Aug 28, 2026
c74c67d
test(compensation-review): reuse shared fixture for string integrity
seonghobae Aug 28, 2026
e5a1e74
test(compensation-review): reuse shared fixture for temporal integrity
seonghobae Aug 28, 2026
d767bef
test(compensation-review): reuse shared fixture for tenant privacy
seonghobae Aug 28, 2026
227926e
test(compensation-review): remove stale datetime import
seonghobae Aug 28, 2026
21fb130
test(compensation): expose mutable timezone evidence drift
seonghobae Aug 29, 2026
729711c
fix(compensation): freeze recorded-time evidence
seonghobae Aug 29, 2026
4b269cc
test(compensation): cover indeterminate timezone offsets
seonghobae Aug 29, 2026
876376f
docs(compensation): trace recorded-time detachment
seonghobae Aug 29, 2026
861274a
docs(compensation): define detached UTC evidence time
seonghobae Aug 29, 2026
faab04e
test(compensation): fail closed on corrupted recorded time
seonghobae Aug 29, 2026
5ed358a
docs(compensation): record timestamp evidence hardening
seonghobae Aug 29, 2026
9eab9d5
docs(compensation): explain stable recorded-time evidence
seonghobae Aug 29, 2026
118a3a3
test(compensation-review): require shared config quality triggers
seonghobae Aug 29, 2026
2842218
fix(compensation-review): retrigger quality on shared config
seonghobae Aug 29, 2026
d753741
docs(compensation-review): record shared-config gate integrity
seonghobae Aug 29, 2026
b9e487c
docs(compensation-review): trace shared-config quality evidence
seonghobae Aug 29, 2026
aba3c1c
chore(compensation-review): adopt protected develop non-destructively
seonghobae Sep 6, 2026
1332776
fix(compensation-review): consolidate quality admission
seonghobae Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/foundation-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
- name: Run owned unit and service contracts once
run: |
PYTHONPATH=packages/candidate-evidence/src COVERAGE_FILE=/tmp/orgmetra-candidate-evidence.coverage python -m pytest -c packages/candidate-evidence/pyproject.toml packages/candidate-evidence/tests
PYTHONPATH=packages/compensation-change-review/src COVERAGE_FILE=/tmp/orgmetra-compensation-change-review.coverage python -m pytest -c packages/compensation-change-review/pyproject.toml packages/compensation-change-review/tests
PYTHONPATH=packages/hris-kernel/src COVERAGE_FILE=/tmp/orgmetra-hris-kernel.coverage python -m pytest -c packages/hris-kernel/pyproject.toml packages/hris-kernel/tests
PYTHONPATH=packages/keyverse-adapter/src COVERAGE_FILE=/tmp/orgmetra-keyverse-adapter.coverage python -m pytest -c packages/keyverse-adapter/pyproject.toml packages/keyverse-adapter/tests
PYTHONPATH=packages/migration-adapter/src COVERAGE_FILE=/tmp/orgmetra-migration-adapter.coverage python -m pytest -c packages/migration-adapter/pyproject.toml packages/migration-adapter/tests
Expand Down
62 changes: 62 additions & 0 deletions docs/adr/0022-governed-compensation-change-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ADR 0022: Govern compensation changes before authoritative mutation

- Status: Proposed (active PR only)
- Date: 2026-08-19

## Context

Compensation changes are high-impact employment actions. A useful enterprise review boundary must correlate the proposed change to authoritative worker scope, the exact current/proposed compensation artifacts, the governing policy, pay-equity review, budget authorization, and payroll handoff without copying salary, wage, bonus, benefit, equity, protected-attribute, or free-form case values into portable evidence.

Syntactically valid Person, Employment, Assignment, policy, actor, or compensation-artifact references do not prove tenant membership, worker scope, policy applicability, actor separation, or effective-date correctness. Opaque identifiers also remain sensitive correlating metadata even when direct identifiers and pay values are absent. The authoritative HRIS already owns tenant identity semantics: protected Orgmetra core accepts canonical non-sentinel operational UUIDs, including UUIDv7. This leaf package therefore must not narrow `tenant_record_id` to UUIDv4. UUIDv4 remains appropriate for packet-owned opaque references where this package owns the correlation-privacy contract.

Recorded-time evidence also crosses an ownership boundary. Python permits a caller to attach a custom `tzinfo` object to an otherwise exact built-in `datetime`; that timezone object can be mutable. Retaining it inside an issued review packet would allow later caller-side state changes to rewrite the apparent UTC instant or invalidate the packet's creation-time digest. The packet therefore must resolve the input offset exactly once and retain only a detached built-in UTC `datetime` as its trust-bearing recorded-time value.

Current primary-source context is recorded in `docs/doctoring/compensation-change-review-references.md`. ISO 30414:2025 provides current human-capital reporting/disclosure context; U.S. EEOC compensation guidance demonstrates why compensation governance must not turn protected-attribute review into uncontrolled evidence copying; U.S. Department of Labor FLSA recordkeeping guidance demonstrates the need for accountable wage/time records. These are governance inputs only, not certification or universal legal-compliance claims.

## Decision

Orgmetra will expose a value-minimized `CompensationChangeReviewPacket` before any authoritative compensation-related HRIS mutation or payroll execution.

The packet binds one authoritative Orgmetra `tenant_record_id` that satisfies the protected-core canonical non-sentinel operational-UUID contract. Packet-owned opaque references for the compensation review, Person, Employment, active Assignment/Job/Position snapshot, current compensation snapshot, proposed compensation plan, compensation policy, pay-equity review, budget authorization, payroll handoff plan, requester, and reviewer remain canonical non-sentinel UUIDv4-backed namespaced references. This preserves interoperability with authoritative UUIDv7 tenant identities while preventing timestamp/node-derived correlation metadata from entering packet-owned references presented as opaque. Evidence artifacts carry independent lowercase SHA-256 digests; a bounded positive `evidence_version`, proposed business effective date, and precision-preserving recorded-time instant are part of canonical evidence.

`generated_at` accepts only an exact built-in timezone-aware `datetime`. Construction obtains a concrete UTC offset once, fails closed when the offset is indeterminate, converts the wall time to the corresponding instant, and stores a detached exact built-in UTC `datetime` backed by `datetime.timezone.utc`. Canonical export therefore never depends on later mutation of a caller-owned timezone object. Datetime subclasses are rejected so caller-overridable conversion or formatting methods cannot forge recorded-time evidence.

The packet deliberately excludes compensation values, protected-attribute values, free-form case narrative, credentials, and free-form model output. It explicitly acknowledges remaining personal-data correlation with `contains_personal_data = true`.

Direct construction and replacement fail closed unless the packet remains:

- `human_confirmation_required = true`;
- `decision_authority = human_review_only`;
- `review_state = requires_human_review`;
- `scope_verification_state = requires_authoritative_resolution`;
- `mutation_state = not_authorized_to_apply`; and
- `external_execution_state = not_authorized_to_execute`.

Requester and reviewer opaque references must differ, but authoritative separation of duties requires both identities to be re-resolved inside the packet tenant immediately before approval. The host must also re-resolve every packet reference, prove Person-to-Employment and active Assignment/Job/Position scope, and verify the current compensation snapshot, proposed plan, exact compensation policy, pay-equity review, budget authorization, effective date, and payroll-handoff provenance without copying compensation or protected-attribute values into the packet. Neither authoritative tenant UUID syntax nor packet-owned UUIDv4 syntax proves tenant membership, actor identity, worker scope, policy applicability, or substantive correctness.

Any authorized HRIS change must use the authoritative Orgmetra People boundary with its own purpose-bound authorization, idempotency, bitemporal persistence, and immutable audit/outbox evidence. Payroll execution remains behind the payroll owner's published contract. This package performs no foreign mutation and no direct cross-service application-table SQL.

## Consequences

### Positive

- A review envelope cannot masquerade as compensation approval, an applied HRIS change, or completed payroll execution.
- Buyers can correlate exact policy/equity/budget/proposed-plan evidence while minimizing duplicated pay and protected-attribute values.
- Authoritative UUIDv7 tenant identities remain interoperable instead of being rejected by a leaf-only UUIDv4 policy.
- UUIDv1 timestamp/node correlation remains excluded from packet-owned namespaced trust references.
- Changes to governed evidence or `evidence_version` change the canonical packet digest.
- Recorded-time evidence is detached from caller-owned mutable timezone state before issuance, so a previously issued canonical payload cannot drift because an input timezone object later changes.
- Cross-tenant, wrong-worker, stale-policy, actor-separation, and effective-date questions remain explicit authoritative-resolution obligations instead of being inferred from reference syntax.

### Trade-offs

- The packet is not anonymous; opaque worker/evidence correlations still require purpose-bound access, retention, export, and audit controls.
- UUID syntax is not authorization, ownership, or relationship evidence; authoritative tenant identity policy and packet-owned reference privacy are intentionally separate ownership boundaries.
- A pay-equity evidence reference/digest proves which artifact was reviewed, not that compensation is fair, nondiscriminatory, or legally sufficient.
- The packet does not calculate compensation, payroll, taxes, protected-class statistics, pay-equity findings, or legal conclusions.
- Hosts must perform authoritative scope and actor resolution at approval time.
- The stored recorded-time representation is normalized to UTC; an input timezone's display-zone identity is intentionally not retained as trust-bearing evidence.

## Verification

The package requires exact 100% owned statement and branch coverage; beginner-readable module/class/callable docstrings; direct-construction and `dataclasses.replace(...)` fail-closed regressions; authoritative tenant interoperability with the protected-core canonical non-sentinel operational-UUID contract, including the canonical UUIDv7 tenant used by the core PostgreSQL regression and rejection of RFC 9562 Nil/Max sentinels; strict UUIDv4 packet-owned namespaced references including UUIDv1 rejection; lowercase SHA-256 evidence; redacted `repr`; closed non-sensitive reason categories; bounded evidence versions; business-date validation; exact built-in timezone-aware timestamp validation; one-time offset resolution with detached UTC storage; indeterminate-offset and datetime-subclass rejection; mutable-timezone post-issuance regression; deterministic canonical JSON/digest evidence; separate requester/reviewer references plus explicit authoritative identity re-resolution; and immutable human-review/no-mutation/no-execution states.
15 changes: 15 additions & 0 deletions docs/doctoring/compensation-change-review-references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Compensation change review references

Verified/retrieved: August 19, 2026.

## APA 7 references

International Organization for Standardization. (2025). *ISO 30414:2025 Human resource management — Requirements and recommendations for human capital reporting and disclosure* (2nd ed.). https://www.iso.org/standard/30414

U.S. Equal Employment Opportunity Commission. (2000). *Section 10: Compensation discrimination*. https://www.eeoc.gov/laws/guidance/section-10-compensation-discrimination

U.S. Department of Labor, Wage and Hour Division. (n.d.). *Fact Sheet #21: Recordkeeping requirements under the Fair Labor Standards Act (FLSA).* Retrieved August 19, 2026, from https://www.dol.gov/agencies/whd/fact-sheets/21-flsa-recordkeeping

## Applied boundary

These sources support a conservative governance boundary: compensation evidence should be versioned, attributable, reviewable, and minimized rather than copied into a broad review envelope. U.S. EEOC and DOL material is jurisdiction-specific context, not a universal rule engine. This package does not infer discrimination, determine legal compliance, calculate pay, perform payroll, or claim ISO certification. It binds only the exact reviewed policy/equity/budget/compensation-plan artifacts and preserves accountable human review before authoritative action.
23 changes: 23 additions & 0 deletions docs/traceability/compensation-change-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Compensation change review traceability

Status: **active PR only / proposed**, not protected-`develop` product truth until merged.

| Buyer or governance need | Contract | Executable evidence |
| --- | --- | --- |
| Correlate a proposed pay change without copying pay amounts | Opaque Person/Employment/current/proposed compensation references + independent digests; compensation/protected values excluded | `test_builds_deterministic_value_minimized_packet`, `test_every_reference_and_digest_is_validated` |
| Preserve authoritative tenant identity while preventing leaf-owned reference correlation smuggling | `tenant_record_id` follows protected HRIS canonical non-sentinel operational UUID semantics; packet-owned namespaced trust references require canonical UUIDv4 | `test_accepts_authoritative_operational_uuidv7_tenant_identity`, `test_rejects_reserved_sentinel_tenant_identity`, `test_invalid_core_inputs_fail_closed`, `test_every_reference_and_digest_is_validated`, `test_uuid1_trust_reference_is_rejected_by_builder_and_replace` |
| Preserve accountable separation of duties | Requester/reviewer references differ; canonical next action requires tenant-scoped identity re-resolution and distinct resolved actors | `test_same_opaque_actor_reference_is_rejected_early`, `test_next_action_requires_identity_scope_and_evidence_before_approval` |
| Prevent review evidence from authorizing a high-impact action | Human-only review, unresolved authoritative scope, no HRIS mutation, no external execution are immutable | `test_direct_constructor_and_replace_fail_closed` |
| Bind policy, equity, budget, payroll handoff, and evidence version | Independent SHA-256 digests and bounded positive `evidence_version` are canonical | `test_canonical_evidence_changes_with_governed_artifact_or_version`, `test_invalid_core_inputs_fail_closed` |
| Prevent an issued packet from emitting rewritten compensation evidence | live object identity is bound to construction-time canonical SHA-256; canonical export snapshots once and rejects later valid-value rewrites or unsupported copies | `test_valid_value_mutation_cannot_rewrite_emitted_evidence`, `test_shallow_copy_does_not_inherit_process_local_issuance_evidence`, `test_collected_packet_releases_process_local_issuance_binding` |
| Avoid accidental logging of worker/pay correlations | Generated dataclass representation disabled; custom representation fully redacted | `test_repr_redacts_personal_and_compensation_correlations` |
| Preserve exact audit time semantics without retaining caller-owned mutable timezone state | Exact built-in timezone-aware `datetime` input is resolved once, detached into a built-in UTC `datetime`, and then used for canonical evidence; unresolved offsets and hostile datetime subclasses fail closed | `test_fractional_and_offset_timestamps_preserve_distinct_instants`, `test_timezone_with_unknown_offset_is_rejected`, `test_rejects_datetime_subclasses_that_can_forge_recorded_time_evidence`, `test_rejects_timezone_without_resolvable_utc_offset`, `test_freezes_mutable_timezone_before_issuing_recorded_time_evidence` |
| Keep package-quality evidence current without recreating leaf CI | Canonical Foundation CI invokes the compensation-review package suite directly on every `develop` PR and the package-specific workflow remains absent | `tests/test_quality_workflow_trigger.py`, `tests.test_github_actions_runner_image`; supplemental to central required workflows |

## Quality gate

`.github/workflows/foundation-ci.yml` checks out the exact PR head on the explicit `ubuntu-24.04` runner, uses the repository's hash-pinned reviewed Python test toolchain, and invokes the compensation-review package suite directly. The package's `pyproject.toml` continues to require exact 100% statement and branch coverage. `.github/workflows/compensation-change-review-quality.yml` is deliberately absent after repository-quality consolidation; executable regressions reject its reintroduction rather than duplicating the canonical Foundation lane.

## Ownership boundary

This slice adds no database migration, compensation calculation, payroll implementation, protected-attribute analytics, direct cross-service SQL, or competing People mutation surface. Authoritative tenant identity plus Person/Employment/Assignment scope and subsequent HRIS mutation remain Orgmetra core responsibilities; external payroll work remains behind its published owner contract. UUIDv4 is an opacity/privacy constraint for packet-owned namespaced references only and does not establish tenant membership, actor identity, worker relationship, or policy applicability. The process-local issuance registry protects only the live Python object and is not durable uniqueness, authorization, attestation, or a replacement for immutable audit/outbox persistence. `docs/doctoring/compensation-change-review-references.md` records current primary-source context without promoting the package into a legal-compliance engine.
6 changes: 3 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"files": [
{
"path": ".github/workflows/foundation-ci.yml",
"sha256": "b6a4365936b66803a8112f034c77d53d33301a7a798ed4f68746a4f2d8b081d7",
"bytes": 6651,
"lines": 125
"sha256": "972ef8929abe93342eb11d000c490e397b44e6e34ddb41d317383ff67067ffe8",
"bytes": 6889,
"lines": 126
},
{
"path": ".gitignore",
Expand Down
18 changes: 18 additions & 0 deletions packages/compensation-change-review/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Changelog

## [Unreleased]

### Added

- `CompensationChangeReviewPacket`, a value-minimized pre-mutation evidence contract that binds authoritative worker scope, reviewed compensation-plan/policy evidence, pay-equity review, budget authorization, and payroll handoff provenance without copying compensation or protected-attribute values.
- Fail-closed human-review, authoritative-resolution, no-HRIS-mutation, and no-payroll-execution states with separate requester/reviewer actor references and bounded evidence versions.
- Deterministic canonical JSON/SHA-256 evidence, redacted representations, canonical non-sentinel UUIDv4 identities for packet-owned namespaced opaque trust references, and exact 100% owned statement/branch coverage regressions.
- Creation-bound process-local evidence integrity: low-level valid-value field rewrites and unsupported object copies cannot emit a second valid-looking canonical compensation-review truth.
- Compensation-review quality now runs inside canonical `Foundation CI`; the package-specific workflow is retired after protected repository-quality consolidation, while the package `pyproject.toml` continues to enforce exact 100% statement and branch coverage.

### Changed

- `tenant_record_id` now follows the authoritative Orgmetra canonical non-sentinel operational-UUID contract instead of imposing a duplicate UUIDv4-only leaf policy; the protected-core UUIDv7 tenant form is covered explicitly while RFC 9562 Nil/Max sentinels remain rejected.
- Canonical export now snapshots all trust-bearing fields once and verifies the exact snapshot against the packet's construction-time SHA-256 seal before returning evidence. This process-local seal is defense in depth only and does not replace durable authorization, signatures, audit/outbox persistence, or correlation uniqueness.
- Digest evidence now rejects `str` subclasses so packet fields cannot retain caller-defined runtime behavior.
- Recorded-time evidence now resolves the input UTC offset once and stores a detached exact built-in UTC `datetime`; caller-owned mutable timezone state cannot rewrite or invalidate an already-issued packet, while indeterminate offsets and datetime subclasses fail closed.
Loading
Loading