From 7640ba7e7a000f2f9a336ec96f2c8d16254bff86 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:04:32 -0700 Subject: [PATCH 01/95] test: scaffold selection monitoring contract --- packages/selection-monitoring/pyproject.toml | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 packages/selection-monitoring/pyproject.toml diff --git a/packages/selection-monitoring/pyproject.toml b/packages/selection-monitoring/pyproject.toml new file mode 100644 index 000000000..016e22d60 --- /dev/null +++ b/packages/selection-monitoring/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["setuptools>=69"] +build-backend = "setuptools.build_meta" + +[project] +name = "orgmetra-selection-monitoring" +version = "0.1.0" +description = "Governed selection-outcome monitoring plan evidence for Orgmetra." +requires-python = ">=3.12" + +[project.optional-dependencies] +test = ["pytest>=8.3", "pytest-cov>=5.0"] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = [ + "--cov=orgmetra_selection_monitoring", + "--cov-branch", + "--cov-report=term-missing", + "--cov-fail-under=100", +] From 523f6139714f2d81480f6727f9a51f991d117e92 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:05:04 -0700 Subject: [PATCH 02/95] test: define governed selection monitoring RED contract --- .../selection-monitoring/tests/test_plan.py | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_plan.py diff --git a/packages/selection-monitoring/tests/test_plan.py b/packages/selection-monitoring/tests/test_plan.py new file mode 100644 index 000000000..3b4c5818e --- /dev/null +++ b/packages/selection-monitoring/tests/test_plan.py @@ -0,0 +1,283 @@ +from __future__ import annotations + +from dataclasses import FrozenInstanceError, replace +from datetime import date, datetime, timedelta, timezone, tzinfo +from hashlib import sha256 +import json + +import pytest + +from orgmetra_selection_monitoring import ( + SelectionOutcomeMonitoringPlan, + build_selection_outcome_monitoring_plan, +) + + +DIGEST_A = "a" * 64 +DIGEST_B = "b" * 64 +DIGEST_C = "c" * 64 +DIGEST_D = "d" * 64 +DIGEST_E = "e" * 64 + + +def valid_kwargs() -> dict[str, object]: + return { + "tenant_record_id": "11111111-1111-4111-8111-111111111111", + "monitoring_plan_reference": "selection_monitoring_plan:plan-001", + "job_profile_reference": "job_profile:job-001", + "selection_process_reference": "selection_process:process-001", + "population_snapshot_reference": "population_snapshot:population-001", + "population_snapshot_digest": DIGEST_A, + "outcome_snapshot_reference": "selection_outcome_snapshot:outcomes-001", + "outcome_snapshot_digest": DIGEST_B, + "protected_attribute_policy_reference": "protected_attribute_policy:policy-001", + "protected_attribute_policy_digest": DIGEST_C, + "small_sample_policy_reference": "small_sample_policy:policy-001", + "small_sample_policy_digest": DIGEST_D, + "statistical_plan_reference": "statistical_plan:plan-001", + "statistical_plan_digest": DIGEST_E, + "actor_reference": "actor:requester-001", + "reviewer_reference": "actor:reviewer-001", + "monitoring_start": date(2026, 1, 1), + "monitoring_end": date(2026, 3, 31), + "purpose_code": "selection_outcome_monitoring", + "reason_code": "quarterly_selection_governance", + "generated_at": datetime(2026, 4, 2, 8, 30, 0, 123456, tzinfo=timezone.utc), + } + + +def build_valid() -> SelectionOutcomeMonitoringPlan: + return build_selection_outcome_monitoring_plan(**valid_kwargs()) + + +def test_builds_aggregate_only_human_review_plan() -> None: + plan = build_valid() + + assert plan.analysis_scope == "total_selection_process_by_job" + assert plan.contains_individual_records is False + assert plan.human_confirmation_required is True + assert plan.decision_authority == "human_review_only" + assert plan.review_state == "requires_human_review" + assert "authorized analyst" in plan.next_action + assert "legal conclusion" in plan.next_action + + +def test_canonical_json_and_digest_are_deterministic_and_value_free() -> None: + plan = build_valid() + payload = json.loads(plan.canonical_json()) + + assert payload["generated_at"] == "2026-04-02T08:30:00.123456Z" + assert payload["monitoring_start"] == "2026-01-01" + assert payload["monitoring_end"] == "2026-03-31" + assert "candidate" not in payload + assert "protected_attribute_value" not in payload + assert plan.sha256_digest() == sha256(plan.canonical_json().encode("utf-8")).hexdigest() + + +def test_fractional_seconds_remain_distinct_evidence() -> None: + first = build_valid() + second = replace( + first, + generated_at=first.generated_at + timedelta(microseconds=1), + ) + + assert first.canonical_json() != second.canonical_json() + assert first.sha256_digest() != second.sha256_digest() + + +@pytest.mark.parametrize( + ("field_name", "value"), + [ + ("tenant_record_id", "not-a-uuid"), + ("tenant_record_id", "00000000-0000-0000-0000-000000000000"), + ("tenant_record_id", "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"), + ("tenant_record_id", None), + ], +) +def test_rejects_nonoperational_tenant_identity(field_name: str, value: object) -> None: + kwargs = valid_kwargs() + kwargs[field_name] = value + with pytest.raises(ValueError, match="tenant_record_id"): + build_selection_outcome_monitoring_plan(**kwargs) + + +@pytest.mark.parametrize( + ("field_name", "value", "message"), + [ + ("monitoring_plan_reference", "wrong:plan-001", "selection_monitoring_plan"), + ("job_profile_reference", "job:job-001", "job_profile"), + ("selection_process_reference", "selection:process-001", "selection_process"), + ("population_snapshot_reference", "population:population-001", "population_snapshot"), + ("outcome_snapshot_reference", "outcome:outcomes-001", "selection_outcome_snapshot"), + ( + "protected_attribute_policy_reference", + "policy:protected-001", + "protected_attribute_policy", + ), + ("small_sample_policy_reference", "policy:small-001", "small_sample_policy"), + ("statistical_plan_reference", "statistics:plan-001", "statistical_plan"), + ("actor_reference", "person:requester-001", "actor"), + ("reviewer_reference", "reviewer:reviewer-001", "actor"), + ("actor_reference", "actor:", "actor"), + ("actor_reference", 1, "actor"), + ("actor_reference", "actor:" + "a" * 155, "actor"), + ], +) +def test_rejects_bad_opaque_references( + field_name: str, + value: object, + message: str, +) -> None: + kwargs = valid_kwargs() + kwargs[field_name] = value + with pytest.raises(ValueError, match=message): + build_selection_outcome_monitoring_plan(**kwargs) + + +@pytest.mark.parametrize( + "field_name", + [ + "population_snapshot_digest", + "outcome_snapshot_digest", + "protected_attribute_policy_digest", + "small_sample_policy_digest", + "statistical_plan_digest", + ], +) +@pytest.mark.parametrize("value", ["A" * 64, "a" * 63, 1]) +def test_rejects_malformed_digests(field_name: str, value: object) -> None: + kwargs = valid_kwargs() + kwargs[field_name] = value + with pytest.raises(ValueError, match="lowercase SHA-256"): + build_selection_outcome_monitoring_plan(**kwargs) + + +def test_reviewer_must_be_distinct_from_requester() -> None: + kwargs = valid_kwargs() + kwargs["reviewer_reference"] = kwargs["actor_reference"] + with pytest.raises(ValueError, match="different accountable actor"): + build_selection_outcome_monitoring_plan(**kwargs) + + +@pytest.mark.parametrize( + ("field_name", "value", "message"), + [ + ("monitoring_start", datetime(2026, 1, 1, tzinfo=timezone.utc), "calendar date"), + ("monitoring_end", datetime(2026, 3, 31, tzinfo=timezone.utc), "calendar date"), + ("monitoring_start", "2026-01-01", "calendar date"), + ("monitoring_end", "2026-03-31", "calendar date"), + ], +) +def test_rejects_non_date_monitoring_bounds( + field_name: str, + value: object, + message: str, +) -> None: + kwargs = valid_kwargs() + kwargs[field_name] = value + with pytest.raises(ValueError, match=message): + build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_reverse_monitoring_window() -> None: + kwargs = valid_kwargs() + kwargs["monitoring_start"] = date(2026, 4, 1) + with pytest.raises(ValueError, match="must not precede"): + build_selection_outcome_monitoring_plan(**kwargs) + + +@pytest.mark.parametrize( + ("field_name", "value", "message"), + [ + ("purpose_code", "selection_review", "selection_outcome_monitoring"), + ("purpose_code", "SelectionOutcomeMonitoring", "lower snake_case"), + ("purpose_code", "a_" + "b" * 64, "lower snake_case"), + ("purpose_code", 1, "lower snake_case"), + ("reason_code", "quarterly", "lower snake_case"), + ("reason_code", "Quarterly_Review", "lower snake_case"), + ("reason_code", 1, "lower snake_case"), + ], +) +def test_rejects_bad_governance_codes( + field_name: str, + value: object, + message: str, +) -> None: + kwargs = valid_kwargs() + kwargs[field_name] = value + with pytest.raises(ValueError, match=message): + build_selection_outcome_monitoring_plan(**kwargs) + + +class NullOffsetTz(tzinfo): + def utcoffset(self, dt: datetime | None) -> None: + return None + + def dst(self, dt: datetime | None) -> None: + return None + + def tzname(self, dt: datetime | None) -> str: + return "NULL" + + +@pytest.mark.parametrize( + "value", + [ + datetime(2026, 4, 2, 8, 30), + "2026-04-02T08:30:00Z", + 1, + datetime(2026, 4, 2, 8, 30).replace(tzinfo=NullOffsetTz()), + ], +) +def test_rejects_nonaware_generation_time(value: object) -> None: + kwargs = valid_kwargs() + kwargs["generated_at"] = value + with pytest.raises(ValueError, match="timezone-aware"): + build_selection_outcome_monitoring_plan(**kwargs) + + +@pytest.mark.parametrize( + ("field_name", "value", "message"), + [ + ("analysis_scope", "component_only", "total_selection_process_by_job"), + ("contains_individual_records", True, "aggregate-only"), + ("contains_individual_records", 0, "aggregate-only"), + ("human_confirmation_required", False, "human confirmation"), + ("human_confirmation_required", 1, "human confirmation"), + ("decision_authority", "automated", "human_review_only"), + ("review_state", "approved", "requires_human_review"), + ("next_action", "Compute adverse impact.", "governed monitoring instruction"), + ], +) +def test_direct_constructor_and_replace_fail_closed( + field_name: str, + value: object, + message: str, +) -> None: + plan = build_valid() + with pytest.raises(ValueError, match=message): + replace(plan, **{field_name: value}) + + +def test_frozen_plan_rejects_mutation() -> None: + plan = build_valid() + with pytest.raises(FrozenInstanceError): + plan.review_state = "approved" + + +def test_timezone_is_normalized_without_losing_precision() -> None: + kwargs = valid_kwargs() + kwargs["generated_at"] = datetime( + 2026, + 4, + 2, + 17, + 30, + 0, + 654321, + tzinfo=timezone(timedelta(hours=9)), + ) + plan = build_selection_outcome_monitoring_plan(**kwargs) + + payload = json.loads(plan.canonical_json()) + assert payload["generated_at"] == "2026-04-02T08:30:00.654321Z" From 83e23e354e3aeebd6f2f09732e4b90ff48159420 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:05:35 -0700 Subject: [PATCH 03/95] feat: implement governed selection monitoring plan --- .../src/orgmetra_selection_monitoring/plan.py | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py new file mode 100644 index 000000000..820aa4e07 --- /dev/null +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -0,0 +1,266 @@ +"""Governed, aggregate-only selection-outcome monitoring plan evidence. + +The packet binds one Job-scoped total selection process to exact aggregate snapshot, +protected-attribute handling, small-sample interpretation, and statistical-plan evidence. +It carries no candidate identities, protected-attribute values, scores, or decisions and +does not itself compute or assert adverse impact. +""" +from __future__ import annotations + +from dataclasses import dataclass +from datetime import date, datetime, timezone +from hashlib import sha256 +import json +import re +from uuid import UUID + +_CODE_PATTERN = re.compile(r"^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$") +_DIGEST_PATTERN = re.compile(r"^[0-9a-f]{64}$") +_REFERENCE_PATTERN = re.compile( + r"^[a-z][a-z0-9_]{1,31}:[A-Za-z0-9](?:[A-Za-z0-9._-]{0,126}[A-Za-z0-9])?$" +) +_PURPOSE_CODE = "selection_outcome_monitoring" +_ANALYSIS_SCOPE = "total_selection_process_by_job" +_REVIEW_STATE = "requires_human_review" +_DECISION_AUTHORITY = "human_review_only" +_NEXT_ACTION = ( + "Verify Job scope, aggregate population completeness, protected-attribute handling, " + "small-sample policy, and statistical-plan provenance; then submit the aggregate " + "evidence to an authorized analyst and accountable human reviewer before any " + "employment-process change or legal conclusion." +) + + +def _validate_operational_uuid(value: str, field_name: str) -> None: + """Require canonical non-sentinel UUID text for a governance identity.""" + try: + parsed = UUID(value) + except (ValueError, AttributeError, TypeError) as exc: + raise ValueError(f"{field_name} must be canonical UUID text") from exc + if str(parsed) != value or parsed.int in (0, (1 << 128) - 1): + raise ValueError(f"{field_name} must be a canonical operational UUID") + + +def _validate_code(value: str, field_name: str) -> None: + """Require a bounded descriptive lower snake_case governance code.""" + if not isinstance(value, str) or len(value) > 64 or not _CODE_PATTERN.fullmatch(value): + raise ValueError(f"{field_name} must be bounded two-or-more-word lower snake_case") + + +def _validate_reference(value: str, prefix: str, field_name: str) -> None: + """Require a bounded namespaced opaque reference with the expected prefix.""" + if ( + not isinstance(value, str) + or len(value) > 160 + or not _REFERENCE_PATTERN.fullmatch(value) + or not value.startswith(f"{prefix}:") + ): + raise ValueError(f"{field_name} must be an opaque {prefix}: reference") + + +def _validate_digest(value: str, field_name: str) -> None: + """Require lowercase SHA-256 hexadecimal evidence.""" + if not isinstance(value, str) or not _DIGEST_PATTERN.fullmatch(value): + raise ValueError(f"{field_name} must be lowercase SHA-256 hex") + + +def _canonical_timestamp(value: datetime) -> str: + """Render an aware instant as precision-preserving UTC RFC 3339 text.""" + if not isinstance(value, datetime) or value.tzinfo is None or value.utcoffset() is None: + raise ValueError("generated_at must be timezone-aware") + return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + + +@dataclass(frozen=True, slots=True) +class SelectionOutcomeMonitoringPlan: + """Immutable aggregate-monitoring plan awaiting accountable human review.""" + + tenant_record_id: str + monitoring_plan_reference: str + job_profile_reference: str + selection_process_reference: str + population_snapshot_reference: str + population_snapshot_digest: str + outcome_snapshot_reference: str + outcome_snapshot_digest: str + protected_attribute_policy_reference: str + protected_attribute_policy_digest: str + small_sample_policy_reference: str + small_sample_policy_digest: str + statistical_plan_reference: str + statistical_plan_digest: str + actor_reference: str + reviewer_reference: str + monitoring_start: date + monitoring_end: date + purpose_code: str + reason_code: str + generated_at: datetime + analysis_scope: str = _ANALYSIS_SCOPE + contains_individual_records: bool = False + human_confirmation_required: bool = True + decision_authority: str = _DECISION_AUTHORITY + review_state: str = _REVIEW_STATE + next_action: str = _NEXT_ACTION + + def __post_init__(self) -> None: + """Fail closed when direct construction drifts from the governed contract.""" + _validate_operational_uuid(self.tenant_record_id, "tenant_record_id") + _validate_reference( + self.monitoring_plan_reference, + "selection_monitoring_plan", + "monitoring_plan_reference", + ) + _validate_reference(self.job_profile_reference, "job_profile", "job_profile_reference") + _validate_reference( + self.selection_process_reference, + "selection_process", + "selection_process_reference", + ) + _validate_reference( + self.population_snapshot_reference, + "population_snapshot", + "population_snapshot_reference", + ) + _validate_digest(self.population_snapshot_digest, "population_snapshot_digest") + _validate_reference( + self.outcome_snapshot_reference, + "selection_outcome_snapshot", + "outcome_snapshot_reference", + ) + _validate_digest(self.outcome_snapshot_digest, "outcome_snapshot_digest") + _validate_reference( + self.protected_attribute_policy_reference, + "protected_attribute_policy", + "protected_attribute_policy_reference", + ) + _validate_digest( + self.protected_attribute_policy_digest, + "protected_attribute_policy_digest", + ) + _validate_reference( + self.small_sample_policy_reference, + "small_sample_policy", + "small_sample_policy_reference", + ) + _validate_digest(self.small_sample_policy_digest, "small_sample_policy_digest") + _validate_reference( + self.statistical_plan_reference, + "statistical_plan", + "statistical_plan_reference", + ) + _validate_digest(self.statistical_plan_digest, "statistical_plan_digest") + _validate_reference(self.actor_reference, "actor", "actor_reference") + _validate_reference(self.reviewer_reference, "actor", "reviewer_reference") + if self.actor_reference == self.reviewer_reference: + raise ValueError("reviewer_reference must identify a different accountable actor") + if not isinstance(self.monitoring_start, date) or isinstance(self.monitoring_start, datetime): + raise ValueError("monitoring_start must be a calendar date") + if not isinstance(self.monitoring_end, date) or isinstance(self.monitoring_end, datetime): + raise ValueError("monitoring_end must be a calendar date") + if self.monitoring_end < self.monitoring_start: + raise ValueError("monitoring_end must not precede monitoring_start") + _validate_code(self.purpose_code, "purpose_code") + if self.purpose_code != _PURPOSE_CODE: + raise ValueError("purpose_code must remain selection_outcome_monitoring") + _validate_code(self.reason_code, "reason_code") + _canonical_timestamp(self.generated_at) + if self.analysis_scope != _ANALYSIS_SCOPE: + raise ValueError("analysis_scope must remain total_selection_process_by_job") + if self.contains_individual_records is not False: + raise ValueError("monitoring plan must remain aggregate-only") + if self.human_confirmation_required is not True: + raise ValueError("human confirmation is mandatory before monitoring use") + if self.decision_authority != _DECISION_AUTHORITY: + raise ValueError("decision_authority must remain human_review_only") + if self.review_state != _REVIEW_STATE: + raise ValueError("review_state must remain requires_human_review") + if self.next_action != _NEXT_ACTION: + raise ValueError("next_action must remain the governed monitoring instruction") + + def canonical_json(self) -> str: + """Return deterministic canonical JSON for immutable audit correlation.""" + payload = { + "actor_reference": self.actor_reference, + "analysis_scope": self.analysis_scope, + "contains_individual_records": self.contains_individual_records, + "decision_authority": self.decision_authority, + "generated_at": _canonical_timestamp(self.generated_at), + "human_confirmation_required": self.human_confirmation_required, + "job_profile_reference": self.job_profile_reference, + "monitoring_end": self.monitoring_end.isoformat(), + "monitoring_plan_reference": self.monitoring_plan_reference, + "monitoring_start": self.monitoring_start.isoformat(), + "next_action": self.next_action, + "outcome_snapshot_digest": self.outcome_snapshot_digest, + "outcome_snapshot_reference": self.outcome_snapshot_reference, + "population_snapshot_digest": self.population_snapshot_digest, + "population_snapshot_reference": self.population_snapshot_reference, + "protected_attribute_policy_digest": self.protected_attribute_policy_digest, + "protected_attribute_policy_reference": self.protected_attribute_policy_reference, + "purpose_code": self.purpose_code, + "reason_code": self.reason_code, + "review_state": self.review_state, + "reviewer_reference": self.reviewer_reference, + "selection_process_reference": self.selection_process_reference, + "small_sample_policy_digest": self.small_sample_policy_digest, + "small_sample_policy_reference": self.small_sample_policy_reference, + "statistical_plan_digest": self.statistical_plan_digest, + "statistical_plan_reference": self.statistical_plan_reference, + "tenant_record_id": self.tenant_record_id, + } + return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + + def sha256_digest(self) -> str: + """Return SHA-256 over the exact canonical UTF-8 monitoring plan.""" + return sha256(self.canonical_json().encode("utf-8")).hexdigest() + + +def build_selection_outcome_monitoring_plan( + *, + tenant_record_id: str, + monitoring_plan_reference: str, + job_profile_reference: str, + selection_process_reference: str, + population_snapshot_reference: str, + population_snapshot_digest: str, + outcome_snapshot_reference: str, + outcome_snapshot_digest: str, + protected_attribute_policy_reference: str, + protected_attribute_policy_digest: str, + small_sample_policy_reference: str, + small_sample_policy_digest: str, + statistical_plan_reference: str, + statistical_plan_digest: str, + actor_reference: str, + reviewer_reference: str, + monitoring_start: date, + monitoring_end: date, + purpose_code: str, + reason_code: str, + generated_at: datetime, +) -> SelectionOutcomeMonitoringPlan: + """Build an aggregate-only monitoring plan pending accountable human review.""" + return SelectionOutcomeMonitoringPlan( + tenant_record_id=tenant_record_id, + monitoring_plan_reference=monitoring_plan_reference, + job_profile_reference=job_profile_reference, + selection_process_reference=selection_process_reference, + population_snapshot_reference=population_snapshot_reference, + population_snapshot_digest=population_snapshot_digest, + outcome_snapshot_reference=outcome_snapshot_reference, + outcome_snapshot_digest=outcome_snapshot_digest, + protected_attribute_policy_reference=protected_attribute_policy_reference, + protected_attribute_policy_digest=protected_attribute_policy_digest, + small_sample_policy_reference=small_sample_policy_reference, + small_sample_policy_digest=small_sample_policy_digest, + statistical_plan_reference=statistical_plan_reference, + statistical_plan_digest=statistical_plan_digest, + actor_reference=actor_reference, + reviewer_reference=reviewer_reference, + monitoring_start=monitoring_start, + monitoring_end=monitoring_end, + purpose_code=purpose_code, + reason_code=reason_code, + generated_at=generated_at, + ) From 9fec4506b8cbb721301f45a7d3d2f1c2ea9eeefe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:06:33 -0700 Subject: [PATCH 04/95] feat: expose selection monitoring contract --- .../src/orgmetra_selection_monitoring/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 packages/selection-monitoring/src/orgmetra_selection_monitoring/__init__.py diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/__init__.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/__init__.py new file mode 100644 index 000000000..efe1d1382 --- /dev/null +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/__init__.py @@ -0,0 +1,8 @@ +"""Public contract for governed selection-outcome monitoring evidence.""" + +from .plan import SelectionOutcomeMonitoringPlan, build_selection_outcome_monitoring_plan + +__all__ = [ + "SelectionOutcomeMonitoringPlan", + "build_selection_outcome_monitoring_plan", +] From 41671e3cfa576cec582a91edbdae844125970406 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:06:51 -0700 Subject: [PATCH 05/95] docs: explain selection monitoring boundary --- packages/selection-monitoring/README.md | 49 +++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 packages/selection-monitoring/README.md diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md new file mode 100644 index 000000000..2bd689841 --- /dev/null +++ b/packages/selection-monitoring/README.md @@ -0,0 +1,49 @@ +# Orgmetra Selection Monitoring + +`orgmetra-selection-monitoring` defines a governed, aggregate-only evidence packet for planning post-selection outcome monitoring. It is intentionally not a statistics engine, legal decision engine, or candidate-level evidence store. + +## What the contract binds + +A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and distinct reviewer, and an explicit monitoring window. + +Every trust-bearing artifact is represented by a bounded opaque reference plus an independent SHA-256 digest. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. + +## Governance boundary + +The packet always remains `requires_human_review`, requires explicit human confirmation, and fixes decision authority to `human_review_only`. Its analysis scope is the total selection process for one Job. It does not calculate selection rates, apply the four-fifths rule, estimate statistical significance, infer discrimination, or make an employment-process change. + +The next action is deliberately operational: verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance; then route the evidence to an authorized analyst and accountable human reviewer before any process change or legal conclusion. + +## Example + +```python +from datetime import date, datetime, timezone + +from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan + +plan = build_selection_outcome_monitoring_plan( + tenant_record_id="11111111-1111-4111-8111-111111111111", + monitoring_plan_reference="selection_monitoring_plan:plan-001", + job_profile_reference="job_profile:job-001", + selection_process_reference="selection_process:process-001", + population_snapshot_reference="population_snapshot:population-001", + population_snapshot_digest="a" * 64, + outcome_snapshot_reference="selection_outcome_snapshot:outcomes-001", + outcome_snapshot_digest="b" * 64, + protected_attribute_policy_reference="protected_attribute_policy:policy-001", + protected_attribute_policy_digest="c" * 64, + small_sample_policy_reference="small_sample_policy:policy-001", + small_sample_policy_digest="d" * 64, + statistical_plan_reference="statistical_plan:plan-001", + statistical_plan_digest="e" * 64, + actor_reference="actor:requester-001", + reviewer_reference="actor:reviewer-001", + monitoring_start=date(2026, 1, 1), + monitoring_end=date(2026, 3, 31), + purpose_code="selection_outcome_monitoring", + reason_code="quarterly_selection_governance", + generated_at=datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc), +) +``` + +This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. From 4239a6c626061711e396d409e7571f7cc531e4f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:06:59 -0700 Subject: [PATCH 06/95] docs: record selection monitoring package change --- packages/selection-monitoring/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 packages/selection-monitoring/CHANGELOG.md diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md new file mode 100644 index 000000000..3d5fe5139 --- /dev/null +++ b/packages/selection-monitoring/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable package changes are recorded here. + +## Unreleased + +- Add a governed, aggregate-only `SelectionOutcomeMonitoringPlan` that binds one Job-scoped total selection process to exact aggregate population/outcome snapshots, protected-attribute handling, small-sample interpretation, statistical-plan provenance, a distinct accountable reviewer, and explicit human review without carrying candidate-level values or making an adverse-impact/legal determination. From a6fd5efae920e0b19759a98e00365011e37124ba Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:07:30 -0700 Subject: [PATCH 07/95] docs: record selection monitoring architecture decision --- ...erned-selection-outcome-monitoring-plan.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/adr/0016-governed-selection-outcome-monitoring-plan.md diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md new file mode 100644 index 000000000..05b93c11a --- /dev/null +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -0,0 +1,37 @@ +# ADR 0016: Governed selection-outcome monitoring plan + +- **Status:** Proposed — active PR only +- **Decision scope:** Selection validity / workforce intelligence governance + +## Context + +Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. + +The EEOC's common interpretation of the Uniform Guidelines directs users to examine the total selection process first for each job, describes the four-fifths rule as a rule of thumb rather than a legal definition, and notes that small samples, statistical significance, practical significance, and other evidence can matter. ISO 30405:2023 also treats reviewing and learning as part of recruitment practice. SIOP's fifth-edition Principles provide the professional validation framework for personnel selection procedures. + +## Decision + +Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that binds: + +- one operational tenant and authoritative Job; +- one total selection-process reference; +- exact aggregate population and selection-outcome snapshot references and SHA-256 digests; +- exact protected-attribute handling, small-sample interpretation, and statistical-analysis plan references and digests; +- an accountable requester and a distinct accountable reviewer; +- an explicit monitoring business-date window and evidence-generation instant. + +The contract is aggregate-only and carries no candidate identity, protected-attribute value, individual assessment score, individual employment decision, or free-form model output. It fixes `analysis_scope` to `total_selection_process_by_job`, `decision_authority` to `human_review_only`, and state to `requires_human_review`. It does not calculate selection rates, mechanically apply the four-fifths heuristic, test statistical significance, infer discrimination, or authorize a process change. + +Any later analytics or persistence boundary must independently enforce purpose-bound authorization, minimum-necessary protected-attribute access, small-sample controls, provenance, immutable audit evidence, and accountable human interpretation. Results are evidence for review, not an automated high-impact employment decision or certification/legal conclusion. + +## Consequences + +- Buyers obtain a deterministic governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. +- The total-process-by-Job scope is explicit before any future component drill-down. +- Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope. +- The four-fifths rule cannot be represented as an automatic pass/fail legal rule by this contract; interpretation remains with authorized analysts and accountable humans. +- Psychometric/statistical production compute remains owned by the appropriate Psychometrics Commons / fast-mlsirm / TEPP contract when those kernels are needed. + +## References + +See `docs/doctoring/selection-outcome-monitoring-references.md`. From f84a1edab575a78c60435c8f257da1555034039d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:07:41 -0700 Subject: [PATCH 08/95] docs: doctor selection monitoring evidence --- .../selection-outcome-monitoring-references.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docs/doctoring/selection-outcome-monitoring-references.md diff --git a/docs/doctoring/selection-outcome-monitoring-references.md b/docs/doctoring/selection-outcome-monitoring-references.md new file mode 100644 index 000000000..c86098cee --- /dev/null +++ b/docs/doctoring/selection-outcome-monitoring-references.md @@ -0,0 +1,17 @@ +# Selection-outcome monitoring references + +These references support ADR 0016 and the bounded selection-monitoring contract. They do not convert Orgmetra into a legal-advice, certification, or automated adverse-impact decision service. + +## APA 7 references + +Equal Employment Opportunity Commission, Office of Personnel Management, Department of Justice, Department of Labor, & Department of the Treasury. (1979, March 2). *Questions and answers to clarify and provide a common interpretation of the Uniform Guidelines on Employee Selection Procedures*. U.S. Equal Employment Opportunity Commission. https://www.eeoc.gov/laws/guidance/questions-and-answers-clarify-and-provide-common-interpretation-uniform-guidelines + +International Organization for Standardization. (2023). *ISO 30405:2023 Human resource management—Guidelines on recruitment* (2nd ed.). https://www.iso.org/standard/79488.html + +Society for Industrial and Organizational Psychology. (2018). Principles for the validation and use of personnel selection procedures (5th ed.). *Industrial and Organizational Psychology, 11*(S1), 1–97. https://doi.org/10.1017/iop.2018.195 + +## Applied evidence boundary + +The EEOC source says adverse impact is examined first for the overall selection process for each job and describes the four-fifths/eighty-percent rule as a practical rule of thumb rather than a legal definition. It also explains that small samples and statistical/practical significance can change interpretation. Consequently, the Orgmetra contract binds a Job-scoped total-process monitoring plan, a separate small-sample policy, and a separate statistical plan but does not itself calculate or adjudicate adverse impact. + +ISO 30405:2023 is the current published second edition and includes reviewing and learning among recruitment practices. SIOP's fifth-edition Principles are used as the professional selection-validation frame; Orgmetra does not duplicate its psychometric methods in this package. From 313a6ce03008f07bc5f385a186dce1e34bb8accb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:07:58 -0700 Subject: [PATCH 09/95] docs: trace selection monitoring contract --- .../selection-outcome-monitoring.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/traceability/selection-outcome-monitoring.md diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md new file mode 100644 index 000000000..be189ffac --- /dev/null +++ b/docs/traceability/selection-outcome-monitoring.md @@ -0,0 +1,26 @@ +# Selection-outcome monitoring traceability + +## Status + +**Active PR / proposed capability.** This file does not describe protected-`develop` behavior until the owning PR is integrated. + +## Buyer need → contract evidence + +| Buyer / governance need | Owned contract evidence | Explicit non-claim | +|---|---|---| +| Monitor the correct hiring/promotion process | Exact `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job` | No component-level causality claim | +| Reproduce the monitored population and outcomes | Exact aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | +| Preserve privacy and interpretation rules | Exact protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | +| Bind the analysis method before interpretation | Exact statistical-plan reference/digest | No statistics are calculated by this package | +| Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, distinct requester/reviewer | No automated employment-process change or legal conclusion | +| Preserve replayable audit correlation | Precision-preserving UTC generation time, canonical JSON, SHA-256 packet digest | Digest proves envelope integrity, not source truth or scientific/legal validity | + +## Executable evidence + +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, reference namespaces, SHA-256 digests, requester/reviewer separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. + +`.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. + +## Ownership boundary + +This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. From 0aea81f768235d62974686f02b9b0a93a30266be Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:08:09 -0700 Subject: [PATCH 10/95] ci: verify selection monitoring exact head --- .../selection-monitoring-quality.yml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/selection-monitoring-quality.yml diff --git a/.github/workflows/selection-monitoring-quality.yml b/.github/workflows/selection-monitoring-quality.yml new file mode 100644 index 000000000..8c7d78160 --- /dev/null +++ b/.github/workflows/selection-monitoring-quality.yml @@ -0,0 +1,57 @@ +name: Selection Monitoring Quality + +on: + pull_request: + branches: + - develop + paths: + - "packages/selection-monitoring/**" + - ".github/requirements/foundation-test.txt" + - ".github/workflows/selection-monitoring-quality.yml" + - "docs/adr/0016-governed-selection-outcome-monitoring-plan.md" + - "docs/doctoring/selection-outcome-monitoring-references.md" + - "docs/traceability/selection-outcome-monitoring.md" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: selection-monitoring-quality-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + unit: + name: Selection monitoring contract and 100% coverage + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout exact candidate + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Prove exact candidate checkout + env: + ORGMETRA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: test "$(git rev-parse HEAD)" = "$ORGMETRA_EXPECTED_HEAD_SHA" + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + check-latest: false + - name: Install reviewed test toolchain + run: | + python -m pip install --require-hashes --no-deps --only-binary=:all: -r .github/requirements/foundation-test.txt + python -m pip check + - name: Compile selection monitoring package + run: python -m compileall -q packages/selection-monitoring/src packages/selection-monitoring/tests + - name: Test selection monitoring with exact statement and branch coverage + env: + PYTHONPATH: packages/selection-monitoring/src + COVERAGE_FILE: /tmp/orgmetra-selection-monitoring.coverage + run: python -m pytest -c packages/selection-monitoring/pyproject.toml packages/selection-monitoring/tests + - name: Require clean checkout + run: | + git diff --exit-code + test -z "$(git status --porcelain)" From 10e104ff8035e74e6a7bbda5ee014b64ca9ee1e6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 19:13:01 -0700 Subject: [PATCH 11/95] test: require authoritative monitoring actor separation --- .../tests/test_actor_separation.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_actor_separation.py diff --git a/packages/selection-monitoring/tests/test_actor_separation.py b/packages/selection-monitoring/tests/test_actor_separation.py new file mode 100644 index 000000000..7dad236c4 --- /dev/null +++ b/packages/selection-monitoring/tests/test_actor_separation.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from datetime import date, datetime, timezone + +import pytest + +from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan + + +def _build(**overrides): + values = { + "tenant_record_id": "11111111-1111-4111-8111-111111111111", + "monitoring_plan_reference": "selection_monitoring_plan:plan-001", + "job_profile_reference": "job_profile:job-001", + "selection_process_reference": "selection_process:process-001", + "population_snapshot_reference": "population_snapshot:population-001", + "population_snapshot_digest": "a" * 64, + "outcome_snapshot_reference": "selection_outcome_snapshot:outcomes-001", + "outcome_snapshot_digest": "b" * 64, + "protected_attribute_policy_reference": "protected_attribute_policy:policy-001", + "protected_attribute_policy_digest": "c" * 64, + "small_sample_policy_reference": "small_sample_policy:policy-001", + "small_sample_policy_digest": "d" * 64, + "statistical_plan_reference": "statistical_plan:plan-001", + "statistical_plan_digest": "e" * 64, + "actor_reference": "actor:requester-001", + "reviewer_reference": "actor:reviewer-001", + "monitoring_start": date(2026, 1, 1), + "monitoring_end": date(2026, 3, 31), + "purpose_code": "selection_outcome_monitoring", + "reason_code": "quarterly_selection_governance", + "generated_at": datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc), + } + values.update(overrides) + return build_selection_outcome_monitoring_plan(**values) + + +def test_requester_and_reviewer_require_authoritative_actor_separation() -> None: + with pytest.raises(ValueError, match="different accountable actor"): + _build(reviewer_reference="actor:requester-001") + + normalized_next_action = _build().next_action.lower() + assert "actor_reference and reviewer_reference" in normalized_next_action + assert "resolved actor identities are distinct" in normalized_next_action From e033229f9509ea0e35ae49448d3b0f7bd3511b90 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 19:13:33 -0700 Subject: [PATCH 12/95] fix: require authoritative monitoring actor separation --- .../src/orgmetra_selection_monitoring/plan.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 820aa4e07..cab1c3cc1 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -24,9 +24,11 @@ _REVIEW_STATE = "requires_human_review" _DECISION_AUTHORITY = "human_review_only" _NEXT_ACTION = ( - "Verify Job scope, aggregate population completeness, protected-attribute handling, " - "small-sample policy, and statistical-plan provenance; then submit the aggregate " - "evidence to an authorized analyst and accountable human reviewer before any " + "Within tenant_record_id, re-resolve actor_reference and reviewer_reference through the " + "authoritative actor boundary and verify their resolved actor identities are distinct; " + "then verify Job scope, aggregate population completeness, protected-attribute handling, " + "small-sample policy, and statistical-plan provenance before submitting the aggregate " + "evidence to an authorized analyst and accountable human reviewer for any " "employment-process change or legal conclusion." ) From 40846374820e58262854da391aeccfd355fbf00d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 19:13:58 -0700 Subject: [PATCH 13/95] docs: require authoritative monitoring actor separation --- packages/selection-monitoring/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 2bd689841..d7d5887b1 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -4,7 +4,7 @@ ## What the contract binds -A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and distinct reviewer, and an explicit monitoring window. +A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, and an explicit monitoring window. Every trust-bearing artifact is represented by a bounded opaque reference plus an independent SHA-256 digest. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. @@ -12,7 +12,7 @@ Every trust-bearing artifact is represented by a bounded opaque reference plus a The packet always remains `requires_human_review`, requires explicit human confirmation, and fixes decision authority to `human_review_only`. Its analysis scope is the total selection process for one Job. It does not calculate selection rates, apply the four-fifths rule, estimate statistical significance, infer discrimination, or make an employment-process change. -The next action is deliberately operational: verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance; then route the evidence to an authorized analyst and accountable human reviewer before any process change or legal conclusion. +Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve `actor_reference` and `reviewer_reference` within the exact `tenant_record_id` through the authoritative actor boundary and prove their resolved actor identities are distinct. It must then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. ## Example @@ -46,4 +46,4 @@ plan = build_selection_outcome_monitoring_plan( ) ``` -This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. +This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, authoritative actor resolution, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. From fb1b20225f209258632fd1cc71960332a81ed836 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 19:14:10 -0700 Subject: [PATCH 14/95] docs: bind monitoring review to resolved actors --- .../0016-governed-selection-outcome-monitoring-plan.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md index 05b93c11a..009271ee3 100644 --- a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -5,7 +5,7 @@ ## Context -Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. +Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. Different opaque requester/reviewer references also do not prove that the authoritative actor boundary resolves them to different accountable people. The EEOC's common interpretation of the Uniform Guidelines directs users to examine the total selection process first for each job, describes the four-fifths rule as a rule of thumb rather than a legal definition, and notes that small samples, statistical significance, practical significance, and other evidence can matter. ISO 30405:2023 also treats reviewing and learning as part of recruitment practice. SIOP's fifth-edition Principles provide the professional validation framework for personnel selection procedures. @@ -17,18 +17,21 @@ Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that b - one total selection-process reference; - exact aggregate population and selection-outcome snapshot references and SHA-256 digests; - exact protected-attribute handling, small-sample interpretation, and statistical-analysis plan references and digests; -- an accountable requester and a distinct accountable reviewer; +- an accountable requester reference and an accountable reviewer reference; - an explicit monitoring business-date window and evidence-generation instant. +The packet rejects identical requester/reviewer references as an early syntactic guard. Before review, the host must re-resolve both actor references within the exact packet tenant through the authoritative actor boundary and reject review use unless the resolved actor identities are distinct. Reference inequality alone is not separation-of-duties evidence. + The contract is aggregate-only and carries no candidate identity, protected-attribute value, individual assessment score, individual employment decision, or free-form model output. It fixes `analysis_scope` to `total_selection_process_by_job`, `decision_authority` to `human_review_only`, and state to `requires_human_review`. It does not calculate selection rates, mechanically apply the four-fifths heuristic, test statistical significance, infer discrimination, or authorize a process change. -Any later analytics or persistence boundary must independently enforce purpose-bound authorization, minimum-necessary protected-attribute access, small-sample controls, provenance, immutable audit evidence, and accountable human interpretation. Results are evidence for review, not an automated high-impact employment decision or certification/legal conclusion. +Any later analytics or persistence boundary must independently enforce purpose-bound authorization, authoritative actor resolution, minimum-necessary protected-attribute access, small-sample controls, provenance, immutable audit evidence, and accountable human interpretation. Results are evidence for review, not an automated high-impact employment decision or certification/legal conclusion. ## Consequences - Buyers obtain a deterministic governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. - The total-process-by-Job scope is explicit before any future component drill-down. - Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope. +- Requester/reviewer separation is proven from authoritative resolved actor identities rather than inferred from different opaque strings. - The four-fifths rule cannot be represented as an automatic pass/fail legal rule by this contract; interpretation remains with authorized analysts and accountable humans. - Psychometric/statistical production compute remains owned by the appropriate Psychometrics Commons / fast-mlsirm / TEPP contract when those kernels are needed. From 66c2f964452474ae91c49ed7931a0e4c01532240 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 19:14:24 -0700 Subject: [PATCH 15/95] docs: trace authoritative monitoring actor separation --- docs/traceability/selection-outcome-monitoring.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index be189ffac..0d241e0a0 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -12,15 +12,16 @@ | Reproduce the monitored population and outcomes | Exact aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | | Preserve privacy and interpretation rules | Exact protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | | Bind the analysis method before interpretation | Exact statistical-plan reference/digest | No statistics are calculated by this package | -| Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, distinct requester/reviewer | No automated employment-process change or legal conclusion | +| Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | +| Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | | Preserve replayable audit correlation | Precision-preserving UTC generation time, canonical JSON, SHA-256 packet digest | Digest proves envelope integrity, not source truth or scientific/legal validity | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, reference namespaces, SHA-256 digests, requester/reviewer separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` additionally requires the immutable next action to resolve requester/reviewer through the authoritative tenant-scoped actor boundary and prove distinct resolved identities. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. ## Ownership boundary -This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. +This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Authoritative actor resolution remains at the host identity boundary; this packet only fails closed by requiring that proof before human review use. From 1794ec6c2c38afcb213c8c1991f43ae07a4d9f4d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:09:07 -0700 Subject: [PATCH 16/95] test: reject value-bearing selection monitoring references --- .../tests/test_reference_privacy.py | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_reference_privacy.py diff --git a/packages/selection-monitoring/tests/test_reference_privacy.py b/packages/selection-monitoring/tests/test_reference_privacy.py new file mode 100644 index 000000000..8faa2664f --- /dev/null +++ b/packages/selection-monitoring/tests/test_reference_privacy.py @@ -0,0 +1,85 @@ +"""Privacy regressions for selection-monitoring opaque references.""" + +from __future__ import annotations + +from dataclasses import replace +from datetime import date, datetime, timezone + +import pytest + +from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan + + +def _build(**overrides): + """Build a valid packet using canonical UUID-backed opaque references.""" + values = { + "tenant_record_id": "11111111-1111-4111-8111-111111111111", + "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + "job_profile_reference": "job_profile:10000000-0000-4000-8000-000000000002", + "selection_process_reference": "selection_process:10000000-0000-4000-8000-000000000003", + "population_snapshot_reference": "population_snapshot:10000000-0000-4000-8000-000000000004", + "population_snapshot_digest": "a" * 64, + "outcome_snapshot_reference": "selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", + "outcome_snapshot_digest": "b" * 64, + "protected_attribute_policy_reference": "protected_attribute_policy:10000000-0000-4000-8000-000000000006", + "protected_attribute_policy_digest": "c" * 64, + "small_sample_policy_reference": "small_sample_policy:10000000-0000-4000-8000-000000000007", + "small_sample_policy_digest": "d" * 64, + "statistical_plan_reference": "statistical_plan:10000000-0000-4000-8000-000000000008", + "statistical_plan_digest": "e" * 64, + "actor_reference": "actor:10000000-0000-4000-8000-000000000009", + "reviewer_reference": "actor:10000000-0000-4000-8000-00000000000a", + "monitoring_start": date(2026, 1, 1), + "monitoring_end": date(2026, 3, 31), + "purpose_code": "selection_outcome_monitoring", + "reason_code": "quarterly_selection_governance", + "generated_at": datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc), + } + values.update(overrides) + return build_selection_outcome_monitoring_plan(**values) + + +@pytest.mark.parametrize( + ("field_name", "value", "message"), + [ + ( + "monitoring_plan_reference", + "selection_monitoring_plan:Quarterly-Plan", + "opaque selection_monitoring_plan", + ), + ("job_profile_reference", "job_profile:RN-ICU", "opaque job_profile"), + ( + "selection_process_reference", + "selection_process:hiring-2026", + "opaque selection_process", + ), + ( + "protected_attribute_policy_reference", + "protected_attribute_policy:race-gender", + "opaque protected_attribute_policy", + ), + ("actor_reference", "actor:seonghobae", "opaque actor"), + ( + "reviewer_reference", + "actor:00000000-0000-0000-0000-000000000000", + "opaque actor", + ), + ( + "population_snapshot_reference", + "population_snapshot:FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF", + "opaque population_snapshot", + ), + ], +) +def test_references_reject_value_bearing_sentinel_and_noncanonical_suffixes( + field_name: str, + value: object, + message: str, +) -> None: + """Reject reference suffixes that can leak values or evade opaque-ID rules.""" + with pytest.raises(ValueError, match=message): + _build(**{field_name: value}) + + packet = _build() + with pytest.raises(ValueError, match=message): + replace(packet, **{field_name: value}) From d9719d5bead2edaa18a00821ecb896978b1be966 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:09:23 -0700 Subject: [PATCH 17/95] test: use opaque UUID actor references --- .../tests/test_actor_separation.py | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/selection-monitoring/tests/test_actor_separation.py b/packages/selection-monitoring/tests/test_actor_separation.py index 7dad236c4..53b6572eb 100644 --- a/packages/selection-monitoring/tests/test_actor_separation.py +++ b/packages/selection-monitoring/tests/test_actor_separation.py @@ -1,3 +1,5 @@ +"""Actor-separation regressions for selection-monitoring review evidence.""" + from __future__ import annotations from datetime import date, datetime, timezone @@ -8,23 +10,24 @@ def _build(**overrides): + """Build a valid monitoring plan with canonical opaque references.""" values = { "tenant_record_id": "11111111-1111-4111-8111-111111111111", - "monitoring_plan_reference": "selection_monitoring_plan:plan-001", - "job_profile_reference": "job_profile:job-001", - "selection_process_reference": "selection_process:process-001", - "population_snapshot_reference": "population_snapshot:population-001", + "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + "job_profile_reference": "job_profile:10000000-0000-4000-8000-000000000002", + "selection_process_reference": "selection_process:10000000-0000-4000-8000-000000000003", + "population_snapshot_reference": "population_snapshot:10000000-0000-4000-8000-000000000004", "population_snapshot_digest": "a" * 64, - "outcome_snapshot_reference": "selection_outcome_snapshot:outcomes-001", + "outcome_snapshot_reference": "selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", "outcome_snapshot_digest": "b" * 64, - "protected_attribute_policy_reference": "protected_attribute_policy:policy-001", + "protected_attribute_policy_reference": "protected_attribute_policy:10000000-0000-4000-8000-000000000006", "protected_attribute_policy_digest": "c" * 64, - "small_sample_policy_reference": "small_sample_policy:policy-001", + "small_sample_policy_reference": "small_sample_policy:10000000-0000-4000-8000-000000000007", "small_sample_policy_digest": "d" * 64, - "statistical_plan_reference": "statistical_plan:plan-001", + "statistical_plan_reference": "statistical_plan:10000000-0000-4000-8000-000000000008", "statistical_plan_digest": "e" * 64, - "actor_reference": "actor:requester-001", - "reviewer_reference": "actor:reviewer-001", + "actor_reference": "actor:10000000-0000-4000-8000-000000000009", + "reviewer_reference": "actor:10000000-0000-4000-8000-00000000000a", "monitoring_start": date(2026, 1, 1), "monitoring_end": date(2026, 3, 31), "purpose_code": "selection_outcome_monitoring", @@ -36,8 +39,9 @@ def _build(**overrides): def test_requester_and_reviewer_require_authoritative_actor_separation() -> None: + """Require both syntactic and authoritative actor separation before review.""" with pytest.raises(ValueError, match="different accountable actor"): - _build(reviewer_reference="actor:requester-001") + _build(reviewer_reference="actor:10000000-0000-4000-8000-000000000009") normalized_next_action = _build().next_action.lower() assert "actor_reference and reviewer_reference" in normalized_next_action From 8d7fa50a1dfe7c44052098918a1647626c4dcf49 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:09:57 -0700 Subject: [PATCH 18/95] test: use opaque UUID monitoring references --- .../selection-monitoring/tests/test_plan.py | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/packages/selection-monitoring/tests/test_plan.py b/packages/selection-monitoring/tests/test_plan.py index 3b4c5818e..f9a14e101 100644 --- a/packages/selection-monitoring/tests/test_plan.py +++ b/packages/selection-monitoring/tests/test_plan.py @@ -1,3 +1,5 @@ +"""Executable contract tests for governed selection-outcome monitoring.""" + from __future__ import annotations from dataclasses import FrozenInstanceError, replace @@ -21,23 +23,24 @@ def valid_kwargs() -> dict[str, object]: + """Return one valid monitoring-plan input using opaque UUID references.""" return { "tenant_record_id": "11111111-1111-4111-8111-111111111111", - "monitoring_plan_reference": "selection_monitoring_plan:plan-001", - "job_profile_reference": "job_profile:job-001", - "selection_process_reference": "selection_process:process-001", - "population_snapshot_reference": "population_snapshot:population-001", + "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + "job_profile_reference": "job_profile:10000000-0000-4000-8000-000000000002", + "selection_process_reference": "selection_process:10000000-0000-4000-8000-000000000003", + "population_snapshot_reference": "population_snapshot:10000000-0000-4000-8000-000000000004", "population_snapshot_digest": DIGEST_A, - "outcome_snapshot_reference": "selection_outcome_snapshot:outcomes-001", + "outcome_snapshot_reference": "selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", "outcome_snapshot_digest": DIGEST_B, - "protected_attribute_policy_reference": "protected_attribute_policy:policy-001", + "protected_attribute_policy_reference": "protected_attribute_policy:10000000-0000-4000-8000-000000000006", "protected_attribute_policy_digest": DIGEST_C, - "small_sample_policy_reference": "small_sample_policy:policy-001", + "small_sample_policy_reference": "small_sample_policy:10000000-0000-4000-8000-000000000007", "small_sample_policy_digest": DIGEST_D, - "statistical_plan_reference": "statistical_plan:plan-001", + "statistical_plan_reference": "statistical_plan:10000000-0000-4000-8000-000000000008", "statistical_plan_digest": DIGEST_E, - "actor_reference": "actor:requester-001", - "reviewer_reference": "actor:reviewer-001", + "actor_reference": "actor:10000000-0000-4000-8000-000000000009", + "reviewer_reference": "actor:10000000-0000-4000-8000-00000000000a", "monitoring_start": date(2026, 1, 1), "monitoring_end": date(2026, 3, 31), "purpose_code": "selection_outcome_monitoring", @@ -47,10 +50,12 @@ def valid_kwargs() -> dict[str, object]: def build_valid() -> SelectionOutcomeMonitoringPlan: + """Build one valid governed monitoring plan.""" return build_selection_outcome_monitoring_plan(**valid_kwargs()) def test_builds_aggregate_only_human_review_plan() -> None: + """Keep the packet aggregate-only and human-review-only.""" plan = build_valid() assert plan.analysis_scope == "total_selection_process_by_job" @@ -63,6 +68,7 @@ def test_builds_aggregate_only_human_review_plan() -> None: def test_canonical_json_and_digest_are_deterministic_and_value_free() -> None: + """Preserve deterministic canonical evidence without individual values.""" plan = build_valid() payload = json.loads(plan.canonical_json()) @@ -75,6 +81,7 @@ def test_canonical_json_and_digest_are_deterministic_and_value_free() -> None: def test_fractional_seconds_remain_distinct_evidence() -> None: + """Keep sub-second evidence instants distinct in canonical evidence.""" first = build_valid() second = replace( first, @@ -95,6 +102,7 @@ def test_fractional_seconds_remain_distinct_evidence() -> None: ], ) def test_rejects_nonoperational_tenant_identity(field_name: str, value: object) -> None: + """Reject malformed, sentinel, and noncanonical tenant UUIDs.""" kwargs = valid_kwargs() kwargs[field_name] = value with pytest.raises(ValueError, match="tenant_record_id"): @@ -128,6 +136,7 @@ def test_rejects_bad_opaque_references( value: object, message: str, ) -> None: + """Reject malformed or wrong-namespace opaque references.""" kwargs = valid_kwargs() kwargs[field_name] = value with pytest.raises(ValueError, match=message): @@ -146,6 +155,7 @@ def test_rejects_bad_opaque_references( ) @pytest.mark.parametrize("value", ["A" * 64, "a" * 63, 1]) def test_rejects_malformed_digests(field_name: str, value: object) -> None: + """Require exact lowercase SHA-256 evidence digests.""" kwargs = valid_kwargs() kwargs[field_name] = value with pytest.raises(ValueError, match="lowercase SHA-256"): @@ -153,6 +163,7 @@ def test_rejects_malformed_digests(field_name: str, value: object) -> None: def test_reviewer_must_be_distinct_from_requester() -> None: + """Reject identical requester and reviewer references.""" kwargs = valid_kwargs() kwargs["reviewer_reference"] = kwargs["actor_reference"] with pytest.raises(ValueError, match="different accountable actor"): @@ -173,6 +184,7 @@ def test_rejects_non_date_monitoring_bounds( value: object, message: str, ) -> None: + """Require business dates rather than datetimes or text.""" kwargs = valid_kwargs() kwargs[field_name] = value with pytest.raises(ValueError, match=message): @@ -180,6 +192,7 @@ def test_rejects_non_date_monitoring_bounds( def test_rejects_reverse_monitoring_window() -> None: + """Reject a monitoring interval whose end precedes its start.""" kwargs = valid_kwargs() kwargs["monitoring_start"] = date(2026, 4, 1) with pytest.raises(ValueError, match="must not precede"): @@ -203,6 +216,7 @@ def test_rejects_bad_governance_codes( value: object, message: str, ) -> None: + """Require fixed purpose plus bounded descriptive governance codes.""" kwargs = valid_kwargs() kwargs[field_name] = value with pytest.raises(ValueError, match=message): @@ -210,13 +224,18 @@ def test_rejects_bad_governance_codes( class NullOffsetTz(tzinfo): + """Timezone fixture whose UTC offset is intentionally unknown.""" + def utcoffset(self, dt: datetime | None) -> None: + """Return no UTC offset.""" return None def dst(self, dt: datetime | None) -> None: + """Return no daylight-saving offset.""" return None def tzname(self, dt: datetime | None) -> str: + """Return a stable fixture timezone label.""" return "NULL" @@ -230,6 +249,7 @@ def tzname(self, dt: datetime | None) -> str: ], ) def test_rejects_nonaware_generation_time(value: object) -> None: + """Require a timezone-aware evidence-generation instant.""" kwargs = valid_kwargs() kwargs["generated_at"] = value with pytest.raises(ValueError, match="timezone-aware"): @@ -254,18 +274,21 @@ def test_direct_constructor_and_replace_fail_closed( value: object, message: str, ) -> None: + """Revalidate immutable governance fields under dataclass replacement.""" plan = build_valid() with pytest.raises(ValueError, match=message): replace(plan, **{field_name: value}) def test_frozen_plan_rejects_mutation() -> None: + """Prevent in-place mutation of a governed monitoring plan.""" plan = build_valid() with pytest.raises(FrozenInstanceError): plan.review_state = "approved" def test_timezone_is_normalized_without_losing_precision() -> None: + """Normalize offsets to UTC while preserving microsecond identity.""" kwargs = valid_kwargs() kwargs["generated_at"] = datetime( 2026, From 5382a9bd155a58551d671cadf2babe3790369009 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:10:22 -0700 Subject: [PATCH 19/95] fix: enforce opaque UUID monitoring references --- .../src/orgmetra_selection_monitoring/plan.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index cab1c3cc1..b5c2e98c5 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -50,14 +50,22 @@ def _validate_code(value: str, field_name: str) -> None: def _validate_reference(value: str, prefix: str, field_name: str) -> None: - """Require a bounded namespaced opaque reference with the expected prefix.""" + """Require an expected namespace plus a canonical operational UUID suffix.""" + error_message = f"{field_name} must be an opaque {prefix}: reference" if ( not isinstance(value, str) or len(value) > 160 or not _REFERENCE_PATTERN.fullmatch(value) or not value.startswith(f"{prefix}:") ): - raise ValueError(f"{field_name} must be an opaque {prefix}: reference") + raise ValueError(error_message) + suffix = value.split(":", 1)[1] + try: + parsed = UUID(suffix) + except (ValueError, AttributeError, TypeError) as exc: + raise ValueError(error_message) from exc + if str(parsed) != suffix or parsed.int in (0, (1 << 128) - 1): + raise ValueError(error_message) def _validate_digest(value: str, field_name: str) -> None: From 21fff3977ec1c04c9680ad64a73669c4296e22b5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:10:36 -0700 Subject: [PATCH 20/95] docs: document opaque UUID monitoring references --- packages/selection-monitoring/README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index d7d5887b1..25e810a5a 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -6,7 +6,7 @@ A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, and an explicit monitoring window. -Every trust-bearing artifact is represented by a bounded opaque reference plus an independent SHA-256 digest. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. +Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUID, plus an independent SHA-256 digest where integrity evidence is required. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. ## Governance boundary @@ -23,21 +23,21 @@ from orgmetra_selection_monitoring import build_selection_outcome_monitoring_pla plan = build_selection_outcome_monitoring_plan( tenant_record_id="11111111-1111-4111-8111-111111111111", - monitoring_plan_reference="selection_monitoring_plan:plan-001", - job_profile_reference="job_profile:job-001", - selection_process_reference="selection_process:process-001", - population_snapshot_reference="population_snapshot:population-001", + monitoring_plan_reference="selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + job_profile_reference="job_profile:10000000-0000-4000-8000-000000000002", + selection_process_reference="selection_process:10000000-0000-4000-8000-000000000003", + population_snapshot_reference="population_snapshot:10000000-0000-4000-8000-000000000004", population_snapshot_digest="a" * 64, - outcome_snapshot_reference="selection_outcome_snapshot:outcomes-001", + outcome_snapshot_reference="selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", outcome_snapshot_digest="b" * 64, - protected_attribute_policy_reference="protected_attribute_policy:policy-001", + protected_attribute_policy_reference="protected_attribute_policy:10000000-0000-4000-8000-000000000006", protected_attribute_policy_digest="c" * 64, - small_sample_policy_reference="small_sample_policy:policy-001", + small_sample_policy_reference="small_sample_policy:10000000-0000-4000-8000-000000000007", small_sample_policy_digest="d" * 64, - statistical_plan_reference="statistical_plan:plan-001", + statistical_plan_reference="statistical_plan:10000000-0000-4000-8000-000000000008", statistical_plan_digest="e" * 64, - actor_reference="actor:requester-001", - reviewer_reference="actor:reviewer-001", + actor_reference="actor:10000000-0000-4000-8000-000000000009", + reviewer_reference="actor:10000000-0000-4000-8000-00000000000a", monitoring_start=date(2026, 1, 1), monitoring_end=date(2026, 3, 31), purpose_code="selection_outcome_monitoring", From d65f9c376efc8fb32766ecff42cbb3b91aef39fb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:10:48 -0700 Subject: [PATCH 21/95] docs: bind monitoring evidence to opaque UUID references --- docs/adr/0016-governed-selection-outcome-monitoring-plan.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md index 009271ee3..556722a42 100644 --- a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -20,6 +20,8 @@ Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that b - an accountable requester reference and an accountable reviewer reference; - an explicit monitoring business-date window and evidence-generation instant. +Every namespaced trust-bearing reference uses a canonical non-sentinel UUID suffix. Human-readable, value-bearing, sentinel, and noncanonical suffixes are rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. + The packet rejects identical requester/reviewer references as an early syntactic guard. Before review, the host must re-resolve both actor references within the exact packet tenant through the authoritative actor boundary and reject review use unless the resolved actor identities are distinct. Reference inequality alone is not separation-of-duties evidence. The contract is aggregate-only and carries no candidate identity, protected-attribute value, individual assessment score, individual employment decision, or free-form model output. It fixes `analysis_scope` to `total_selection_process_by_job`, `decision_authority` to `human_review_only`, and state to `requires_human_review`. It does not calculate selection rates, mechanically apply the four-fifths heuristic, test statistical significance, infer discrimination, or authorize a process change. @@ -30,7 +32,7 @@ Any later analytics or persistence boundary must independently enforce purpose-b - Buyers obtain a deterministic governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. - The total-process-by-Job scope is explicit before any future component drill-down. -- Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope. +- Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and opaque reference fields cannot carry value-bearing suffixes. - Requester/reviewer separation is proven from authoritative resolved actor identities rather than inferred from different opaque strings. - The four-fifths rule cannot be represented as an automatic pass/fail legal rule by this contract; interpretation remains with authorized analysts and accountable humans. - Psychometric/statistical production compute remains owned by the appropriate Psychometrics Commons / fast-mlsirm / TEPP contract when those kernels are needed. From fb47b856963c230d9eed2cfff8ae3235adcf010f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:10:59 -0700 Subject: [PATCH 22/95] docs: trace opaque monitoring reference hardening --- docs/traceability/selection-outcome-monitoring.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index 0d241e0a0..0b7cc4d11 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -8,17 +8,18 @@ | Buyer / governance need | Owned contract evidence | Explicit non-claim | |---|---|---| -| Monitor the correct hiring/promotion process | Exact `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job` | No component-level causality claim | -| Reproduce the monitored population and outcomes | Exact aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | -| Preserve privacy and interpretation rules | Exact protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | -| Bind the analysis method before interpretation | Exact statistical-plan reference/digest | No statistics are calculated by this package | +| Monitor the correct hiring/promotion process | Exact UUID-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job` | No component-level causality claim | +| Reproduce the monitored population and outcomes | Exact UUID-backed aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | +| Preserve privacy and interpretation rules | Exact UUID-backed protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | +| Prevent semantic/value smuggling through opaque IDs | Canonical non-sentinel UUID suffix required for every namespaced reference; `test_reference_privacy.py` covers value-bearing, sentinel, noncanonical, builder, and `dataclasses.replace(...)` paths | UUID syntax does not prove source truth or authorization | +| Bind the analysis method before interpretation | Exact UUID-backed statistical-plan reference/digest | No statistics are calculated by this package | | Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | | Preserve replayable audit correlation | Precision-preserving UTC generation time, canonical JSON, SHA-256 packet digest | Digest proves envelope integrity, not source truth or scientific/legal validity | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` additionally requires the immutable next action to resolve requester/reviewer through the authoritative tenant-scoped actor boundary and prove distinct resolved identities. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to resolve requester/reviewer through the authoritative tenant-scoped actor boundary and prove distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting human-readable/value-bearing, sentinel, and noncanonical opaque-reference suffixes through both public construction and replacement paths. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. From 9c72d214fe1bef0ee1f0b49924d6de95ff1dc906 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 22:11:06 -0700 Subject: [PATCH 23/95] docs: record opaque reference hardening --- packages/selection-monitoring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 3d5fe5139..062b86968 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -5,3 +5,4 @@ All notable package changes are recorded here. ## Unreleased - Add a governed, aggregate-only `SelectionOutcomeMonitoringPlan` that binds one Job-scoped total selection process to exact aggregate population/outcome snapshots, protected-attribute handling, small-sample interpretation, statistical-plan provenance, a distinct accountable reviewer, and explicit human review without carrying candidate-level values or making an adverse-impact/legal determination. +- Require every namespaced trust-bearing reference to use a canonical non-sentinel UUID suffix, rejecting human-readable, value-bearing, sentinel, and noncanonical suffixes through both construction and replacement paths. From ed5b451215e3f53946747cb05e1022080c05c58e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 01:24:09 -0700 Subject: [PATCH 24/95] test: close selection monitoring metadata privacy boundary --- .../tests/test_privacy.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_privacy.py diff --git a/packages/selection-monitoring/tests/test_privacy.py b/packages/selection-monitoring/tests/test_privacy.py new file mode 100644 index 000000000..85b02f9af --- /dev/null +++ b/packages/selection-monitoring/tests/test_privacy.py @@ -0,0 +1,60 @@ +"""Privacy regressions for aggregate selection-outcome monitoring evidence.""" + +from dataclasses import replace +from datetime import date, datetime, timezone + +import pytest + +from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan + + +def _build(): + """Build one valid aggregate monitoring plan for privacy-focused assertions.""" + return build_selection_outcome_monitoring_plan( + tenant_record_id="11111111-1111-4111-8111-111111111111", + monitoring_plan_reference="selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + job_profile_reference="job_profile:10000000-0000-4000-8000-000000000002", + selection_process_reference="selection_process:10000000-0000-4000-8000-000000000003", + population_snapshot_reference="population_snapshot:10000000-0000-4000-8000-000000000004", + population_snapshot_digest="a" * 64, + outcome_snapshot_reference="selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", + outcome_snapshot_digest="b" * 64, + protected_attribute_policy_reference="protected_attribute_policy:10000000-0000-4000-8000-000000000006", + protected_attribute_policy_digest="c" * 64, + small_sample_policy_reference="small_sample_policy:10000000-0000-4000-8000-000000000007", + small_sample_policy_digest="d" * 64, + statistical_plan_reference="statistical_plan:10000000-0000-4000-8000-000000000008", + statistical_plan_digest="e" * 64, + actor_reference="actor:10000000-0000-4000-8000-000000000009", + reviewer_reference="actor:10000000-0000-4000-8000-00000000000a", + monitoring_start=date(2026, 1, 1), + monitoring_end=date(2026, 3, 31), + purpose_code="selection_outcome_monitoring", + reason_code="quarterly_selection_governance", + generated_at=datetime(2026, 4, 2, 8, 30, 0, 123456, tzinfo=timezone.utc), + ) + + +@pytest.mark.parametrize( + "reason_code", + ["jane_doe", "salary_120000", "race_gender_review", "candidate_alice_smith"], +) +def test_reason_code_rejects_personal_or_value_bearing_free_form_codes(reason_code: str) -> None: + """Prevent governance reason metadata from becoming an individual-data channel.""" + plan = _build() + with pytest.raises(ValueError): + replace(plan, reason_code=reason_code) + + +def test_repr_redacts_selection_monitoring_correlations() -> None: + """Keep Job, actor, policy, and statistical correlations out of routine repr output.""" + plan = _build() + rendered = repr(plan) + assert rendered == "SelectionOutcomeMonitoringPlan()" + for sensitive in ( + plan.job_profile_reference, + plan.actor_reference, + plan.protected_attribute_policy_reference, + plan.statistical_plan_digest, + ): + assert sensitive not in rendered From 0e661c13fa94c872e9bbe80c50eb3f2ab3c2a9f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 01:24:44 -0700 Subject: [PATCH 25/95] fix: close selection monitoring metadata privacy boundary --- .../src/orgmetra_selection_monitoring/plan.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index b5c2e98c5..ea2add379 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -23,6 +23,7 @@ _ANALYSIS_SCOPE = "total_selection_process_by_job" _REVIEW_STATE = "requires_human_review" _DECISION_AUTHORITY = "human_review_only" +_ALLOWED_REASON_CODES = frozenset({"quarterly_selection_governance"}) _NEXT_ACTION = ( "Within tenant_record_id, re-resolve actor_reference and reviewer_reference through the " "authoritative actor boundary and verify their resolved actor identities are distinct; " @@ -81,7 +82,7 @@ def _canonical_timestamp(value: datetime) -> str: return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") -@dataclass(frozen=True, slots=True) +@dataclass(frozen=True, slots=True, repr=False) class SelectionOutcomeMonitoringPlan: """Immutable aggregate-monitoring plan awaiting accountable human review.""" @@ -174,6 +175,8 @@ def __post_init__(self) -> None: if self.purpose_code != _PURPOSE_CODE: raise ValueError("purpose_code must remain selection_outcome_monitoring") _validate_code(self.reason_code, "reason_code") + if self.reason_code not in _ALLOWED_REASON_CODES: + raise ValueError("reason_code must use a reviewed non-sensitive monitoring reason") _canonical_timestamp(self.generated_at) if self.analysis_scope != _ANALYSIS_SCOPE: raise ValueError("analysis_scope must remain total_selection_process_by_job") @@ -188,6 +191,10 @@ def __post_init__(self) -> None: if self.next_action != _NEXT_ACTION: raise ValueError("next_action must remain the governed monitoring instruction") + def __repr__(self) -> str: + """Return a fully redacted representation safe for routine logs and assertions.""" + return "SelectionOutcomeMonitoringPlan()" + def canonical_json(self) -> str: """Return deterministic canonical JSON for immutable audit correlation.""" payload = { From ba3346e10f0133cdeeaf10eec6be37b59b204290 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 01:25:13 -0700 Subject: [PATCH 26/95] docs: harden selection monitoring metadata guidance --- packages/selection-monitoring/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 25e810a5a..36c7c3a15 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -6,7 +6,9 @@ A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, and an explicit monitoring window. -Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUID, plus an independent SHA-256 digest where integrity evidence is required. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. +Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUID, plus an independent SHA-256 digest where integrity evidence is required. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. + +The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. ## Governance boundary From d81260c918659115b9c2a2b1a152afd59d5f6acf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 02:17:22 -0700 Subject: [PATCH 27/95] test: require monitoring evidence versioning --- .../tests/test_evidence_version.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_evidence_version.py diff --git a/packages/selection-monitoring/tests/test_evidence_version.py b/packages/selection-monitoring/tests/test_evidence_version.py new file mode 100644 index 000000000..30c0ca596 --- /dev/null +++ b/packages/selection-monitoring/tests/test_evidence_version.py @@ -0,0 +1,39 @@ +"""Regression coverage for explicit selection-monitoring evidence versioning.""" + +from dataclasses import replace +import json + +import pytest + +from orgmetra_selection_monitoring import SelectionOutcomeMonitoringPlan + +from test_plan import valid_kwargs + + +def _plan(evidence_version: int = 1) -> SelectionOutcomeMonitoringPlan: + """Build one valid monitoring plan while varying only its evidence version.""" + kwargs = valid_kwargs() + kwargs["evidence_version"] = evidence_version + return SelectionOutcomeMonitoringPlan(**kwargs) + + +def test_evidence_version_is_part_of_immutable_monitoring_evidence() -> None: + """Bind evidence revision identity into canonical JSON and the packet digest.""" + first = _plan(1) + second = _plan(2) + assert json.loads(first.canonical_json())["evidence_version"] == 1 + assert first.canonical_json() != second.canonical_json() + assert first.sha256_digest() != second.sha256_digest() + + +@pytest.mark.parametrize("evidence_version", [True, False, 0, -1, 2_147_483_648, "1", 1.0]) +def test_rejects_noncanonical_evidence_versions(evidence_version: object) -> None: + """Reject booleans, non-integers, non-positive values, and signed-int32 overflow.""" + with pytest.raises(ValueError, match="evidence_version"): + _plan(evidence_version) # type: ignore[arg-type] + + +def test_replace_cannot_bypass_monitoring_evidence_version_validation() -> None: + """Revalidate explicit evidence-version bounds when immutable plans are copied.""" + with pytest.raises(ValueError, match="evidence_version"): + replace(_plan(), evidence_version=0) From 9354688f7af9dfdcc1528eb77e95211c5e8e1f1c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 02:18:00 -0700 Subject: [PATCH 28/95] fix: version selection monitoring evidence --- .../src/orgmetra_selection_monitoring/plan.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index ea2add379..ae2ddd867 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -107,6 +107,7 @@ class SelectionOutcomeMonitoringPlan: purpose_code: str reason_code: str generated_at: datetime + evidence_version: int = 1 analysis_scope: str = _ANALYSIS_SCOPE contains_individual_records: bool = False human_confirmation_required: bool = True @@ -178,6 +179,8 @@ def __post_init__(self) -> None: if self.reason_code not in _ALLOWED_REASON_CODES: raise ValueError("reason_code must use a reviewed non-sensitive monitoring reason") _canonical_timestamp(self.generated_at) + if type(self.evidence_version) is not int or not 1 <= self.evidence_version <= 2_147_483_647: + raise ValueError("evidence_version must be an integer from 1 through 2147483647") if self.analysis_scope != _ANALYSIS_SCOPE: raise ValueError("analysis_scope must remain total_selection_process_by_job") if self.contains_individual_records is not False: @@ -202,6 +205,7 @@ def canonical_json(self) -> str: "analysis_scope": self.analysis_scope, "contains_individual_records": self.contains_individual_records, "decision_authority": self.decision_authority, + "evidence_version": self.evidence_version, "generated_at": _canonical_timestamp(self.generated_at), "human_confirmation_required": self.human_confirmation_required, "job_profile_reference": self.job_profile_reference, @@ -256,6 +260,7 @@ def build_selection_outcome_monitoring_plan( purpose_code: str, reason_code: str, generated_at: datetime, + evidence_version: int = 1, ) -> SelectionOutcomeMonitoringPlan: """Build an aggregate-only monitoring plan pending accountable human review.""" return SelectionOutcomeMonitoringPlan( @@ -280,4 +285,5 @@ def build_selection_outcome_monitoring_plan( purpose_code=purpose_code, reason_code=reason_code, generated_at=generated_at, + evidence_version=evidence_version, ) From 129107878b333e42deef657ab1e648fefcb48a25 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 02:19:46 -0700 Subject: [PATCH 29/95] docs: bind monitoring evidence version --- packages/selection-monitoring/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 36c7c3a15..264fe0c85 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -4,9 +4,9 @@ ## What the contract binds -A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, and an explicit monitoring window. +A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, an explicit monitoring window, and a bounded positive `evidence_version`. -Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUID, plus an independent SHA-256 digest where integrity evidence is required. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. +Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUID, plus an independent SHA-256 digest where integrity evidence is required. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. @@ -45,6 +45,7 @@ plan = build_selection_outcome_monitoring_plan( purpose_code="selection_outcome_monitoring", reason_code="quarterly_selection_governance", generated_at=datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc), + evidence_version=1, ) ``` From de9531409c91e545c87cd68a89094c78f12c9e21 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 02:20:05 -0700 Subject: [PATCH 30/95] docs: trace monitoring evidence versioning --- docs/traceability/selection-outcome-monitoring.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index 0b7cc4d11..05d2c18ac 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -13,13 +13,14 @@ | Preserve privacy and interpretation rules | Exact UUID-backed protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | | Prevent semantic/value smuggling through opaque IDs | Canonical non-sentinel UUID suffix required for every namespaced reference; `test_reference_privacy.py` covers value-bearing, sentinel, noncanonical, builder, and `dataclasses.replace(...)` paths | UUID syntax does not prove source truth or authorization | | Bind the analysis method before interpretation | Exact UUID-backed statistical-plan reference/digest | No statistics are calculated by this package | +| Version actor/purpose/reason evidence explicitly | `evidence_version` is a true positive integer through signed-int32 max and participates in canonical JSON/SHA-256 | `test_evidence_version.py` proves presence, digest separation, bounds, and `dataclasses.replace(...)` revalidation | | Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | | Preserve replayable audit correlation | Precision-preserving UTC generation time, canonical JSON, SHA-256 packet digest | Digest proves envelope integrity, not source truth or scientific/legal validity | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to resolve requester/reviewer through the authoritative tenant-scoped actor boundary and prove distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting human-readable/value-bearing, sentinel, and noncanonical opaque-reference suffixes through both public construction and replacement paths. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to resolve requester/reviewer through the authoritative tenant-scoped actor boundary and prove distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting human-readable/value-bearing, sentinel, and noncanonical opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. From 7f12f3bb0810bbe0161ac033c22ad24c7589e9d3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 02:20:20 -0700 Subject: [PATCH 31/95] docs: record monitoring evidence version decision --- docs/adr/0016-governed-selection-outcome-monitoring-plan.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md index 556722a42..c50c2a2a8 100644 --- a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -18,9 +18,10 @@ Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that b - exact aggregate population and selection-outcome snapshot references and SHA-256 digests; - exact protected-attribute handling, small-sample interpretation, and statistical-analysis plan references and digests; - an accountable requester reference and an accountable reviewer reference; +- fixed purpose and reviewed reason metadata plus a bounded positive `evidence_version` that is part of canonical evidence; - an explicit monitoring business-date window and evidence-generation instant. -Every namespaced trust-bearing reference uses a canonical non-sentinel UUID suffix. Human-readable, value-bearing, sentinel, and noncanonical suffixes are rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. +Every namespaced trust-bearing reference uses a canonical non-sentinel UUID suffix. Human-readable, value-bearing, sentinel, and noncanonical suffixes are rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. `evidence_version` must be a true integer from 1 through 2147483647; changing it changes canonical JSON and the packet SHA-256, so revisions to actor/purpose/reason-bound evidence cannot silently collide. The packet rejects identical requester/reviewer references as an early syntactic guard. Before review, the host must re-resolve both actor references within the exact packet tenant through the authoritative actor boundary and reject review use unless the resolved actor identities are distinct. Reference inequality alone is not separation-of-duties evidence. @@ -30,7 +31,7 @@ Any later analytics or persistence boundary must independently enforce purpose-b ## Consequences -- Buyers obtain a deterministic governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. +- Buyers obtain a deterministic, explicitly versioned governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. - The total-process-by-Job scope is explicit before any future component drill-down. - Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and opaque reference fields cannot carry value-bearing suffixes. - Requester/reviewer separation is proven from authoritative resolved actor identities rather than inferred from different opaque strings. From 0f8afd4fd4acfb1a9414cfc2df9de2a1c6608bf2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 02:20:29 -0700 Subject: [PATCH 32/95] chore: note monitoring evidence versioning --- packages/selection-monitoring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 062b86968..2aae53964 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -6,3 +6,4 @@ All notable package changes are recorded here. - Add a governed, aggregate-only `SelectionOutcomeMonitoringPlan` that binds one Job-scoped total selection process to exact aggregate population/outcome snapshots, protected-attribute handling, small-sample interpretation, statistical-plan provenance, a distinct accountable reviewer, and explicit human review without carrying candidate-level values or making an adverse-impact/legal determination. - Require every namespaced trust-bearing reference to use a canonical non-sentinel UUID suffix, rejecting human-readable, value-bearing, sentinel, and noncanonical suffixes through both construction and replacement paths. +- Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. From f3018e88d55628b9839172c009664a5b8f24bab5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 05:20:49 -0700 Subject: [PATCH 33/95] test(selection-monitoring): require tenant-scoped reference resolution --- .../tests/test_actor_separation.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/tests/test_actor_separation.py b/packages/selection-monitoring/tests/test_actor_separation.py index 53b6572eb..e7a9816fd 100644 --- a/packages/selection-monitoring/tests/test_actor_separation.py +++ b/packages/selection-monitoring/tests/test_actor_separation.py @@ -1,4 +1,4 @@ -"""Actor-separation regressions for selection-monitoring review evidence.""" +"""Actor-separation and tenant-scope regressions for selection-monitoring evidence.""" from __future__ import annotations @@ -46,3 +46,17 @@ def test_requester_and_reviewer_require_authoritative_actor_separation() -> None normalized_next_action = _build().next_action.lower() assert "actor_reference and reviewer_reference" in normalized_next_action assert "resolved actor identities are distinct" in normalized_next_action + + +def test_review_requires_every_reference_to_resolve_in_the_exact_tenant() -> None: + """Prevent cross-tenant evidence mixing behind otherwise valid opaque references.""" + action = _build().next_action + tenant_clause = "re-resolve every packet reference within tenant_record_id" + actor_clause = "verify their resolved actor identities are distinct" + job_clause = "verify Job scope" + review_clause = "accountable human reviewer" + + assert tenant_clause in action + assert action.index(tenant_clause) < action.index(actor_clause) + assert action.index(actor_clause) < action.index(job_clause) + assert action.index(job_clause) < action.index(review_clause) From d15608675e33a748bf887da7745c6357be340f8b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 05:21:41 -0700 Subject: [PATCH 34/95] fix(selection-monitoring): bind all evidence to tenant scope --- .../src/orgmetra_selection_monitoring/plan.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index ae2ddd867..12105dbd5 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -25,12 +25,13 @@ _DECISION_AUTHORITY = "human_review_only" _ALLOWED_REASON_CODES = frozenset({"quarterly_selection_governance"}) _NEXT_ACTION = ( - "Within tenant_record_id, re-resolve actor_reference and reviewer_reference through the " - "authoritative actor boundary and verify their resolved actor identities are distinct; " - "then verify Job scope, aggregate population completeness, protected-attribute handling, " - "small-sample policy, and statistical-plan provenance before submitting the aggregate " - "evidence to an authorized analyst and accountable human reviewer for any " - "employment-process change or legal conclusion." + "Within tenant_record_id, re-resolve every packet reference through its authoritative " + "boundary; specifically re-resolve actor_reference and reviewer_reference and verify " + "their resolved actor identities are distinct; then verify Job scope, aggregate " + "population completeness, protected-attribute handling, small-sample policy, and " + "statistical-plan provenance before submitting the aggregate evidence to an authorized " + "analyst and accountable human reviewer for any employment-process change or legal " + "conclusion." ) From 9681cec093edbf32ab8ff0bacfb1f5a7e3889617 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 05:22:07 -0700 Subject: [PATCH 35/95] docs(selection-monitoring): align tenant reference boundary --- packages/selection-monitoring/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 264fe0c85..2c0a404f2 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -14,7 +14,7 @@ The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan The packet always remains `requires_human_review`, requires explicit human confirmation, and fixes decision authority to `human_review_only`. Its analysis scope is the total selection process for one Job. It does not calculate selection rates, apply the four-fifths rule, estimate statistical significance, infer discrimination, or make an employment-process change. -Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve `actor_reference` and `reviewer_reference` within the exact `tenant_record_id` through the authoritative actor boundary and prove their resolved actor identities are distinct. It must then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. +Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary so a syntactically valid reference from another tenant cannot be mixed into the monitoring envelope. It must specifically re-resolve `actor_reference` and `reviewer_reference` and prove their resolved actor identities are distinct, then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. ## Example @@ -49,4 +49,4 @@ plan = build_selection_outcome_monitoring_plan( ) ``` -This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, authoritative actor resolution, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. +This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, authoritative tenant-scoped reference and actor resolution, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. From 91343cd89e54519da198caed25b3c13d3154b879 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 05:22:22 -0700 Subject: [PATCH 36/95] docs(selection-monitoring): require tenant-scoped evidence resolution --- .../adr/0016-governed-selection-outcome-monitoring-plan.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md index c50c2a2a8..535dcb44a 100644 --- a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -5,7 +5,7 @@ ## Context -Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. Different opaque requester/reviewer references also do not prove that the authoritative actor boundary resolves them to different accountable people. +Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. Different opaque requester/reviewer references also do not prove that the authoritative actor boundary resolves them to different accountable people, and valid UUID-backed references alone do not prove that all referenced evidence belongs to the packet tenant. The EEOC's common interpretation of the Uniform Guidelines directs users to examine the total selection process first for each job, describes the four-fifths rule as a rule of thumb rather than a legal definition, and notes that small samples, statistical significance, practical significance, and other evidence can matter. ISO 30405:2023 also treats reviewing and learning as part of recruitment practice. SIOP's fifth-edition Principles provide the professional validation framework for personnel selection procedures. @@ -23,17 +23,18 @@ Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that b Every namespaced trust-bearing reference uses a canonical non-sentinel UUID suffix. Human-readable, value-bearing, sentinel, and noncanonical suffixes are rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. `evidence_version` must be a true integer from 1 through 2147483647; changing it changes canonical JSON and the packet SHA-256, so revisions to actor/purpose/reason-bound evidence cannot silently collide. -The packet rejects identical requester/reviewer references as an early syntactic guard. Before review, the host must re-resolve both actor references within the exact packet tenant through the authoritative actor boundary and reject review use unless the resolved actor identities are distinct. Reference inequality alone is not separation-of-duties evidence. +Reference syntax is not tenant authority. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through the relevant authoritative boundary and reject review use if any reference belongs to another tenant or cannot be authoritatively resolved. The packet also rejects identical requester/reviewer references as an early syntactic guard; after tenant-scoped resolution, the host must prove that the two references resolve to distinct actor identities. Reference inequality alone is not separation-of-duties evidence. The contract is aggregate-only and carries no candidate identity, protected-attribute value, individual assessment score, individual employment decision, or free-form model output. It fixes `analysis_scope` to `total_selection_process_by_job`, `decision_authority` to `human_review_only`, and state to `requires_human_review`. It does not calculate selection rates, mechanically apply the four-fifths heuristic, test statistical significance, infer discrimination, or authorize a process change. -Any later analytics or persistence boundary must independently enforce purpose-bound authorization, authoritative actor resolution, minimum-necessary protected-attribute access, small-sample controls, provenance, immutable audit evidence, and accountable human interpretation. Results are evidence for review, not an automated high-impact employment decision or certification/legal conclusion. +Any later analytics or persistence boundary must independently enforce purpose-bound authorization, authoritative tenant-scoped reference and actor resolution, minimum-necessary protected-attribute access, small-sample controls, provenance, immutable audit evidence, and accountable human interpretation. Results are evidence for review, not an automated high-impact employment decision or certification/legal conclusion. ## Consequences - Buyers obtain a deterministic, explicitly versioned governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. - The total-process-by-Job scope is explicit before any future component drill-down. - Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and opaque reference fields cannot carry value-bearing suffixes. +- Cross-tenant evidence mixing is fail-closed at the host review boundary because every opaque reference must be re-resolved in the exact packet tenant. - Requester/reviewer separation is proven from authoritative resolved actor identities rather than inferred from different opaque strings. - The four-fifths rule cannot be represented as an automatic pass/fail legal rule by this contract; interpretation remains with authorized analysts and accountable humans. - Psychometric/statistical production compute remains owned by the appropriate Psychometrics Commons / fast-mlsirm / TEPP contract when those kernels are needed. From aeda59046e1b395249e37a217a8040158ebc1a4c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 05:22:38 -0700 Subject: [PATCH 37/95] docs(selection-monitoring): trace tenant-scope regression --- docs/traceability/selection-outcome-monitoring.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index 05d2c18ac..ec3c4baf4 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -8,10 +8,11 @@ | Buyer / governance need | Owned contract evidence | Explicit non-claim | |---|---|---| -| Monitor the correct hiring/promotion process | Exact UUID-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job` | No component-level causality claim | +| Monitor the correct hiring/promotion process | Exact UUID-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job`; immutable next action requires every packet reference to be re-resolved within exact `tenant_record_id` | UUID syntax alone is not tenant authority or component-level causality evidence | | Reproduce the monitored population and outcomes | Exact UUID-backed aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | | Preserve privacy and interpretation rules | Exact UUID-backed protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | | Prevent semantic/value smuggling through opaque IDs | Canonical non-sentinel UUID suffix required for every namespaced reference; `test_reference_privacy.py` covers value-bearing, sentinel, noncanonical, builder, and `dataclasses.replace(...)` paths | UUID syntax does not prove source truth or authorization | +| Prevent cross-tenant evidence mixing | `test_actor_separation.py` requires the governed next action to re-resolve every packet reference within `tenant_record_id` before actor separation, Job scope verification, or accountable review | The packet does not itself query authoritative stores | | Bind the analysis method before interpretation | Exact UUID-backed statistical-plan reference/digest | No statistics are calculated by this package | | Version actor/purpose/reason evidence explicitly | `evidence_version` is a true positive integer through signed-int32 max and participates in canonical JSON/SHA-256 | `test_evidence_version.py` proves presence, digest separation, bounds, and `dataclasses.replace(...)` revalidation | | Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | @@ -20,10 +21,10 @@ ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to resolve requester/reviewer through the authoritative tenant-scoped actor boundary and prove distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting human-readable/value-bearing, sentinel, and noncanonical opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting human-readable/value-bearing, sentinel, and noncanonical opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. ## Ownership boundary -This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Authoritative actor resolution remains at the host identity boundary; this packet only fails closed by requiring that proof before human review use. +This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Authoritative tenant-scoped reference and actor resolution remains at the host boundary; this packet fails closed by requiring that proof before human review use. From 25904db262cbdfd4e0f5361b3518269c825c3014 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 05:22:53 -0700 Subject: [PATCH 38/95] docs(selection-monitoring): record tenant binding repair --- packages/selection-monitoring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 2aae53964..0595b730a 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -6,4 +6,5 @@ All notable package changes are recorded here. - Add a governed, aggregate-only `SelectionOutcomeMonitoringPlan` that binds one Job-scoped total selection process to exact aggregate population/outcome snapshots, protected-attribute handling, small-sample interpretation, statistical-plan provenance, a distinct accountable reviewer, and explicit human review without carrying candidate-level values or making an adverse-impact/legal determination. - Require every namespaced trust-bearing reference to use a canonical non-sentinel UUID suffix, rejecting human-readable, value-bearing, sentinel, and noncanonical suffixes through both construction and replacement paths. +- Require every packet reference to be re-resolved within the exact tenant through its authoritative boundary before actor separation, Job-scope verification, or accountable review, preventing cross-tenant evidence mixing behind valid opaque UUIDs. - Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. From b0ee3662dd678970b7223ca249e7d28e4a8e36bd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 07:44:57 -0700 Subject: [PATCH 39/95] test(selection-monitoring): align tenant next-action assertion --- packages/selection-monitoring/tests/test_actor_separation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/selection-monitoring/tests/test_actor_separation.py b/packages/selection-monitoring/tests/test_actor_separation.py index e7a9816fd..a1f5a4534 100644 --- a/packages/selection-monitoring/tests/test_actor_separation.py +++ b/packages/selection-monitoring/tests/test_actor_separation.py @@ -51,7 +51,7 @@ def test_requester_and_reviewer_require_authoritative_actor_separation() -> None def test_review_requires_every_reference_to_resolve_in_the_exact_tenant() -> None: """Prevent cross-tenant evidence mixing behind otherwise valid opaque references.""" action = _build().next_action - tenant_clause = "re-resolve every packet reference within tenant_record_id" + tenant_clause = "Within tenant_record_id, re-resolve every packet reference" actor_clause = "verify their resolved actor identities are distinct" job_clause = "verify Job scope" review_clause = "accountable human reviewer" From 2207c3e1697f525e69d014006372478dc2b3c002 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 19:52:50 -0700 Subject: [PATCH 40/95] test: reject UUIDv1 selection monitoring references --- .../tests/test_reference_privacy.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/tests/test_reference_privacy.py b/packages/selection-monitoring/tests/test_reference_privacy.py index 8faa2664f..d978c3fb8 100644 --- a/packages/selection-monitoring/tests/test_reference_privacy.py +++ b/packages/selection-monitoring/tests/test_reference_privacy.py @@ -9,9 +9,11 @@ from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan +UUID1_ID = "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + def _build(**overrides): - """Build a valid packet using canonical UUID-backed opaque references.""" + """Build a valid packet using canonical UUIDv4-backed opaque references.""" values = { "tenant_record_id": "11111111-1111-4111-8111-111111111111", "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", @@ -83,3 +85,31 @@ def test_references_reject_value_bearing_sentinel_and_noncanonical_suffixes( packet = _build() with pytest.raises(ValueError, match=message): replace(packet, **{field_name: value}) + + +@pytest.mark.parametrize( + ("field_name", "prefix"), + [ + ("monitoring_plan_reference", "selection_monitoring_plan"), + ("job_profile_reference", "job_profile"), + ("selection_process_reference", "selection_process"), + ("population_snapshot_reference", "population_snapshot"), + ("outcome_snapshot_reference", "selection_outcome_snapshot"), + ("protected_attribute_policy_reference", "protected_attribute_policy"), + ("small_sample_policy_reference", "small_sample_policy"), + ("statistical_plan_reference", "statistical_plan"), + ("actor_reference", "actor"), + ("reviewer_reference", "actor"), + ], +) +def test_uuid1_trust_reference_is_rejected_by_builder_and_replace( + field_name: str, + prefix: str, +) -> None: + """UUIDv1 timestamp/node metadata must never enter an aggregate trust-reference field.""" + value = f"{prefix}:{UUID1_ID}" + with pytest.raises(ValueError, match=field_name): + _build(**{field_name: value}) + + with pytest.raises(ValueError, match=field_name): + replace(_build(), **{field_name: value}) From 46d33303967e8f78225b4e126c51067d22a8c856 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 19:53:20 -0700 Subject: [PATCH 41/95] fix: require UUIDv4 selection monitoring references --- .../src/orgmetra_selection_monitoring/plan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 12105dbd5..6f92457d5 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -52,7 +52,7 @@ def _validate_code(value: str, field_name: str) -> None: def _validate_reference(value: str, prefix: str, field_name: str) -> None: - """Require an expected namespace plus a canonical operational UUID suffix.""" + """Require an expected namespace plus a canonical opaque UUIDv4 suffix.""" error_message = f"{field_name} must be an opaque {prefix}: reference" if ( not isinstance(value, str) @@ -66,7 +66,7 @@ def _validate_reference(value: str, prefix: str, field_name: str) -> None: parsed = UUID(suffix) except (ValueError, AttributeError, TypeError) as exc: raise ValueError(error_message) from exc - if str(parsed) != suffix or parsed.int in (0, (1 << 128) - 1): + if str(parsed) != suffix or parsed.version != 4 or parsed.int in (0, (1 << 128) - 1): raise ValueError(error_message) From 9615799384045a62eb97a4df6238c200d3fd1508 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 19:53:58 -0700 Subject: [PATCH 42/95] docs: define UUIDv4 selection monitoring references --- packages/selection-monitoring/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 2c0a404f2..da66e1286 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -6,7 +6,7 @@ A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, an explicit monitoring window, and a bounded positive `evidence_version`. -Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUID, plus an independent SHA-256 digest where integrity evidence is required. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. +Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUIDv4, plus an independent SHA-256 digest where integrity evidence is required. UUIDv1 and every other UUID version are rejected so timestamp/node-derived correlation metadata cannot enter an otherwise opaque field. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. @@ -14,7 +14,7 @@ The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan The packet always remains `requires_human_review`, requires explicit human confirmation, and fixes decision authority to `human_review_only`. Its analysis scope is the total selection process for one Job. It does not calculate selection rates, apply the four-fifths rule, estimate statistical significance, infer discrimination, or make an employment-process change. -Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary so a syntactically valid reference from another tenant cannot be mixed into the monitoring envelope. It must specifically re-resolve `actor_reference` and `reviewer_reference` and prove their resolved actor identities are distinct, then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. +Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary so a syntactically valid UUIDv4 reference from another tenant cannot be mixed into the monitoring envelope. It must specifically re-resolve `actor_reference` and `reviewer_reference` and prove their resolved actor identities are distinct, then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. UUIDv4 constrains opacity only; it does not establish tenant ownership, actor identity, or evidence validity. ## Example @@ -49,4 +49,4 @@ plan = build_selection_outcome_monitoring_plan( ) ``` -This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, authoritative tenant-scoped reference and actor resolution, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. +This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, authoritative tenant-scoped reference and actor resolution, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. \ No newline at end of file From a1183a6b78611d86f534737da759abce4c78a736 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 00:45:57 -0700 Subject: [PATCH 43/95] test: reject correlating tenant UUIDv1 in selection monitoring --- .../tests/test_reference_privacy.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/tests/test_reference_privacy.py b/packages/selection-monitoring/tests/test_reference_privacy.py index d978c3fb8..5226b03de 100644 --- a/packages/selection-monitoring/tests/test_reference_privacy.py +++ b/packages/selection-monitoring/tests/test_reference_privacy.py @@ -41,6 +41,15 @@ def _build(**overrides): return build_selection_outcome_monitoring_plan(**values) +def test_uuid1_tenant_identity_is_rejected_by_builder_and_replace() -> None: + """UUIDv1 timestamp/node metadata must not enter the public tenant identity.""" + with pytest.raises(ValueError, match="tenant_record_id"): + _build(tenant_record_id=UUID1_ID) + + with pytest.raises(ValueError, match="tenant_record_id"): + replace(_build(), tenant_record_id=UUID1_ID) + + @pytest.mark.parametrize( ("field_name", "value", "message"), [ @@ -112,4 +121,4 @@ def test_uuid1_trust_reference_is_rejected_by_builder_and_replace( _build(**{field_name: value}) with pytest.raises(ValueError, match=field_name): - replace(_build(), **{field_name: value}) + replace(_build(), **{field_name: value}) \ No newline at end of file From 3dca553bf6b07c2afed0dd106dff0c44f0be7f15 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 00:46:36 -0700 Subject: [PATCH 44/95] fix: require opaque UUIDv4 tenant identity in selection monitoring --- .../src/orgmetra_selection_monitoring/plan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 6f92457d5..769c35cf0 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -36,13 +36,13 @@ def _validate_operational_uuid(value: str, field_name: str) -> None: - """Require canonical non-sentinel UUID text for a governance identity.""" + """Require canonical UUIDv4 text so a public governance identity stays opaque.""" try: parsed = UUID(value) except (ValueError, AttributeError, TypeError) as exc: raise ValueError(f"{field_name} must be canonical UUID text") from exc - if str(parsed) != value or parsed.int in (0, (1 << 128) - 1): - raise ValueError(f"{field_name} must be a canonical operational UUID") + if str(parsed) != value or parsed.version != 4 or parsed.int in (0, (1 << 128) - 1): + raise ValueError(f"{field_name} must be a canonical operational UUIDv4") def _validate_code(value: str, field_name: str) -> None: @@ -287,4 +287,4 @@ def build_selection_outcome_monitoring_plan( reason_code=reason_code, generated_at=generated_at, evidence_version=evidence_version, - ) + ) \ No newline at end of file From 7ce805b989aecac615e82e4fd5be2e3a41f6bd56 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 00:47:02 -0700 Subject: [PATCH 45/95] docs: bind selection-monitoring tenant identity to UUIDv4 opacity --- packages/selection-monitoring/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index da66e1286..95b226a67 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -6,15 +6,15 @@ A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, an explicit monitoring window, and a bounded positive `evidence_version`. -Every trust-bearing artifact is represented by a bounded namespaced reference whose suffix is a canonical non-sentinel UUIDv4, plus an independent SHA-256 digest where integrity evidence is required. UUIDv1 and every other UUID version are rejected so timestamp/node-derived correlation metadata cannot enter an otherwise opaque field. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. +The public `tenant_record_id` and every trust-bearing artifact use canonical non-sentinel UUIDv4 identity; namespaced artifact references additionally require their expected namespace. UUIDv1 and every other UUID version are rejected so timestamp/node-derived correlation metadata cannot enter otherwise opaque public identity fields. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. Content-bearing evidence adds an independent SHA-256 digest where integrity evidence is required. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. -The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. +The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose tenant, Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. ## Governance boundary The packet always remains `requires_human_review`, requires explicit human confirmation, and fixes decision authority to `human_review_only`. Its analysis scope is the total selection process for one Job. It does not calculate selection rates, apply the four-fifths rule, estimate statistical significance, infer discrimination, or make an employment-process change. -Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary so a syntactically valid UUIDv4 reference from another tenant cannot be mixed into the monitoring envelope. It must specifically re-resolve `actor_reference` and `reviewer_reference` and prove their resolved actor identities are distinct, then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. UUIDv4 constrains opacity only; it does not establish tenant ownership, actor identity, or evidence validity. +Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary so a syntactically valid UUIDv4 reference from another tenant cannot be mixed into the monitoring envelope. It must specifically re-resolve `actor_reference` and `reviewer_reference` and prove their resolved actor identities are distinct, then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. UUIDv4 constrains public-identity opacity only; it does not establish tenant ownership, actor identity, or evidence validity. ## Example From 2ece2096112380e2f3c405ed290804885845e942 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 00:47:22 -0700 Subject: [PATCH 46/95] docs: require UUIDv4 tenant opacity in selection monitoring ADR --- ...016-governed-selection-outcome-monitoring-plan.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md index 535dcb44a..971aa9cb8 100644 --- a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -5,7 +5,7 @@ ## Context -Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. Different opaque requester/reviewer references also do not prove that the authoritative actor boundary resolves them to different accountable people, and valid UUID-backed references alone do not prove that all referenced evidence belongs to the packet tenant. +Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. Different opaque requester/reviewer references also do not prove that the authoritative actor boundary resolves them to different accountable people, and valid UUID-backed references alone do not prove that all referenced evidence belongs to the packet tenant. UUIDv1 additionally embeds timestamp/node-derived correlation metadata, making it unsuitable for a public tenant identity or a field presented as an opaque trust reference. The EEOC's common interpretation of the Uniform Guidelines directs users to examine the total selection process first for each job, describes the four-fifths rule as a rule of thumb rather than a legal definition, and notes that small samples, statistical significance, practical significance, and other evidence can matter. ISO 30405:2023 also treats reviewing and learning as part of recruitment practice. SIOP's fifth-edition Principles provide the professional validation framework for personnel selection procedures. @@ -13,7 +13,7 @@ The EEOC's common interpretation of the Uniform Guidelines directs users to exam Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that binds: -- one operational tenant and authoritative Job; +- one canonical UUIDv4 operational tenant and authoritative Job; - one total selection-process reference; - exact aggregate population and selection-outcome snapshot references and SHA-256 digests; - exact protected-attribute handling, small-sample interpretation, and statistical-analysis plan references and digests; @@ -21,9 +21,9 @@ Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that b - fixed purpose and reviewed reason metadata plus a bounded positive `evidence_version` that is part of canonical evidence; - an explicit monitoring business-date window and evidence-generation instant. -Every namespaced trust-bearing reference uses a canonical non-sentinel UUID suffix. Human-readable, value-bearing, sentinel, and noncanonical suffixes are rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. `evidence_version` must be a true integer from 1 through 2147483647; changing it changes canonical JSON and the packet SHA-256, so revisions to actor/purpose/reason-bound evidence cannot silently collide. +The public `tenant_record_id` and every namespaced trust-bearing reference use canonical non-sentinel UUIDv4 identity; namespaced references additionally require their expected prefix. UUIDv1 and every other UUID version fail closed so timestamp/node-derived correlation metadata cannot enter public identity fields represented as opaque. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. `evidence_version` must be a true integer from 1 through 2147483647; changing it changes canonical JSON and the packet SHA-256, so revisions to actor/purpose/reason-bound evidence cannot silently collide. -Reference syntax is not tenant authority. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through the relevant authoritative boundary and reject review use if any reference belongs to another tenant or cannot be authoritatively resolved. The packet also rejects identical requester/reviewer references as an early syntactic guard; after tenant-scoped resolution, the host must prove that the two references resolve to distinct actor identities. Reference inequality alone is not separation-of-duties evidence. +UUID syntax is not tenant authority. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through the relevant authoritative boundary and reject review use if any reference belongs to another tenant or cannot be authoritatively resolved. The packet also rejects identical requester/reviewer references as an early syntactic guard; after tenant-scoped resolution, the host must prove that the two references resolve to distinct actor identities. Reference inequality alone is not separation-of-duties evidence. The contract is aggregate-only and carries no candidate identity, protected-attribute value, individual assessment score, individual employment decision, or free-form model output. It fixes `analysis_scope` to `total_selection_process_by_job`, `decision_authority` to `human_review_only`, and state to `requires_human_review`. It does not calculate selection rates, mechanically apply the four-fifths heuristic, test statistical significance, infer discrimination, or authorize a process change. @@ -33,7 +33,7 @@ Any later analytics or persistence boundary must independently enforce purpose-b - Buyers obtain a deterministic, explicitly versioned governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. - The total-process-by-Job scope is explicit before any future component drill-down. -- Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and opaque reference fields cannot carry value-bearing suffixes. +- Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and public tenant/reference identities cannot carry UUIDv1 timestamp/node metadata or value-bearing suffixes. - Cross-tenant evidence mixing is fail-closed at the host review boundary because every opaque reference must be re-resolved in the exact packet tenant. - Requester/reviewer separation is proven from authoritative resolved actor identities rather than inferred from different opaque strings. - The four-fifths rule cannot be represented as an automatic pass/fail legal rule by this contract; interpretation remains with authorized analysts and accountable humans. @@ -41,4 +41,4 @@ Any later analytics or persistence boundary must independently enforce purpose-b ## References -See `docs/doctoring/selection-outcome-monitoring-references.md`. +See `docs/doctoring/selection-outcome-monitoring-references.md`. \ No newline at end of file From 670645c7bf38f180a20bc1d2c2a78c9ab14b95ab Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 00:47:42 -0700 Subject: [PATCH 47/95] docs: trace UUIDv4 tenant opacity in selection monitoring --- docs/traceability/selection-outcome-monitoring.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index ec3c4baf4..4e574508b 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -8,12 +8,12 @@ | Buyer / governance need | Owned contract evidence | Explicit non-claim | |---|---|---| -| Monitor the correct hiring/promotion process | Exact UUID-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job`; immutable next action requires every packet reference to be re-resolved within exact `tenant_record_id` | UUID syntax alone is not tenant authority or component-level causality evidence | -| Reproduce the monitored population and outcomes | Exact UUID-backed aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | -| Preserve privacy and interpretation rules | Exact UUID-backed protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | -| Prevent semantic/value smuggling through opaque IDs | Canonical non-sentinel UUID suffix required for every namespaced reference; `test_reference_privacy.py` covers value-bearing, sentinel, noncanonical, builder, and `dataclasses.replace(...)` paths | UUID syntax does not prove source truth or authorization | +| Monitor the correct hiring/promotion process | Canonical UUIDv4 `tenant_record_id`, exact UUIDv4-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job`; immutable next action requires every packet reference to be re-resolved within exact `tenant_record_id` | UUID syntax alone is not tenant authority or component-level causality evidence | +| Reproduce the monitored population and outcomes | Exact UUIDv4-backed aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | +| Preserve privacy and interpretation rules | Exact UUIDv4-backed protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | +| Prevent semantic/value/correlation smuggling through public IDs | `tenant_record_id` and every governed reference require canonical non-sentinel UUIDv4 identity; namespaced references also require the expected prefix; `test_reference_privacy.py` covers UUIDv1 tenant identity plus value-bearing, sentinel, noncanonical and UUIDv1 reference cases through builder and `dataclasses.replace(...)` paths | UUIDv4 syntax does not prove source truth, tenant membership, or authorization | | Prevent cross-tenant evidence mixing | `test_actor_separation.py` requires the governed next action to re-resolve every packet reference within `tenant_record_id` before actor separation, Job scope verification, or accountable review | The packet does not itself query authoritative stores | -| Bind the analysis method before interpretation | Exact UUID-backed statistical-plan reference/digest | No statistics are calculated by this package | +| Bind the analysis method before interpretation | Exact UUIDv4-backed statistical-plan reference/digest | No statistics are calculated by this package | | Version actor/purpose/reason evidence explicitly | `evidence_version` is a true positive integer through signed-int32 max and participates in canonical JSON/SHA-256 | `test_evidence_version.py` proves presence, digest separation, bounds, and `dataclasses.replace(...)` revalidation | | Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | @@ -21,10 +21,10 @@ ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting human-readable/value-bearing, sentinel, and noncanonical opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting UUIDv1 public tenant identity plus human-readable/value-bearing, sentinel, noncanonical, and non-v4 opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. ## Ownership boundary -This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Authoritative tenant-scoped reference and actor resolution remains at the host boundary; this packet fails closed by requiring that proof before human review use. +This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Authoritative tenant-scoped reference and actor resolution remains at the host boundary; this packet fails closed by requiring that proof before human review use. \ No newline at end of file From 28b5e9e096c057fd825cf9ff2aa8e9ec5611401f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 00:47:54 -0700 Subject: [PATCH 48/95] docs: record selection-monitoring tenant UUIDv4 hardening --- packages/selection-monitoring/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 0595b730a..1a8ffae4a 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -5,6 +5,6 @@ All notable package changes are recorded here. ## Unreleased - Add a governed, aggregate-only `SelectionOutcomeMonitoringPlan` that binds one Job-scoped total selection process to exact aggregate population/outcome snapshots, protected-attribute handling, small-sample interpretation, statistical-plan provenance, a distinct accountable reviewer, and explicit human review without carrying candidate-level values or making an adverse-impact/legal determination. -- Require every namespaced trust-bearing reference to use a canonical non-sentinel UUID suffix, rejecting human-readable, value-bearing, sentinel, and noncanonical suffixes through both construction and replacement paths. +- Require the public `tenant_record_id` and every namespaced trust-bearing reference to use canonical non-sentinel UUIDv4 identity, rejecting UUIDv1 timestamp/node correlation metadata as well as human-readable, value-bearing, sentinel, noncanonical, and other non-v4 reference suffixes through construction and replacement paths. - Require every packet reference to be re-resolved within the exact tenant through its authoritative boundary before actor separation, Job-scope verification, or accountable review, preventing cross-tenant evidence mixing behind valid opaque UUIDs. -- Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. +- Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. \ No newline at end of file From f86c89794f3bcfcba1c58b009faeab2d3c2c30de Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:18:42 -0700 Subject: [PATCH 49/95] test: require selection monitoring to accept core tenant UUIDv7 --- .../tests/test_reference_privacy.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/selection-monitoring/tests/test_reference_privacy.py b/packages/selection-monitoring/tests/test_reference_privacy.py index 5226b03de..b3b8afd22 100644 --- a/packages/selection-monitoring/tests/test_reference_privacy.py +++ b/packages/selection-monitoring/tests/test_reference_privacy.py @@ -10,6 +10,7 @@ from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan UUID1_ID = "6ba7b810-9dad-11d1-80b4-00c04fd430c8" +UUID7_TENANT = "10000000-0000-7000-8000-000000000001" def _build(**overrides): @@ -41,13 +42,13 @@ def _build(**overrides): return build_selection_outcome_monitoring_plan(**values) -def test_uuid1_tenant_identity_is_rejected_by_builder_and_replace() -> None: - """UUIDv1 timestamp/node metadata must not enter the public tenant identity.""" - with pytest.raises(ValueError, match="tenant_record_id"): - _build(tenant_record_id=UUID1_ID) +def test_authoritative_uuid7_tenant_identity_is_accepted_by_builder_and_replace() -> None: + """The monitoring leaf must accept tenant UUIDs already valid in authoritative core.""" + packet = _build(tenant_record_id=UUID7_TENANT) + replaced = replace(_build(), tenant_record_id=UUID7_TENANT) - with pytest.raises(ValueError, match="tenant_record_id"): - replace(_build(), tenant_record_id=UUID1_ID) + assert packet.tenant_record_id == UUID7_TENANT + assert replaced.tenant_record_id == UUID7_TENANT @pytest.mark.parametrize( @@ -121,4 +122,4 @@ def test_uuid1_trust_reference_is_rejected_by_builder_and_replace( _build(**{field_name: value}) with pytest.raises(ValueError, match=field_name): - replace(_build(), **{field_name: value}) \ No newline at end of file + replace(_build(), **{field_name: value}) From 884e2291eb286cf7ebef25df9f87795220ee6c08 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:19:22 -0700 Subject: [PATCH 50/95] fix: honor authoritative tenant UUID contract in selection monitoring --- .../src/orgmetra_selection_monitoring/plan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 769c35cf0..199c93815 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -36,13 +36,13 @@ def _validate_operational_uuid(value: str, field_name: str) -> None: - """Require canonical UUIDv4 text so a public governance identity stays opaque.""" + """Require canonical non-sentinel UUID text owned by the authoritative HRIS.""" try: parsed = UUID(value) except (ValueError, AttributeError, TypeError) as exc: raise ValueError(f"{field_name} must be canonical UUID text") from exc - if str(parsed) != value or parsed.version != 4 or parsed.int in (0, (1 << 128) - 1): - raise ValueError(f"{field_name} must be a canonical operational UUIDv4") + if str(parsed) != value or parsed.int in (0, (1 << 128) - 1): + raise ValueError(f"{field_name} must be a canonical operational UUID") def _validate_code(value: str, field_name: str) -> None: @@ -287,4 +287,4 @@ def build_selection_outcome_monitoring_plan( reason_code=reason_code, generated_at=generated_at, evidence_version=evidence_version, - ) \ No newline at end of file + ) From 6f4b026b9fbc594b216176ee5dc328377f01324f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:19:47 -0700 Subject: [PATCH 51/95] docs: align monitoring tenant identity with core --- packages/selection-monitoring/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 95b226a67..521ace5aa 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -6,7 +6,7 @@ A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, an explicit monitoring window, and a bounded positive `evidence_version`. -The public `tenant_record_id` and every trust-bearing artifact use canonical non-sentinel UUIDv4 identity; namespaced artifact references additionally require their expected namespace. UUIDv1 and every other UUID version are rejected so timestamp/node-derived correlation metadata cannot enter otherwise opaque public identity fields. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. Content-bearing evidence adds an independent SHA-256 digest where integrity evidence is required. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. +`tenant_record_id` follows Orgmetra's authoritative canonical non-sentinel operational UUID contract instead of imposing a second UUID-version policy at this leaf package. Packet-owned trust-bearing artifacts remain canonical non-sentinel UUIDv4 identities; namespaced artifact references additionally require their expected namespace. UUIDv1 and other non-v4 suffixes are rejected for those references so timestamp/node-derived or otherwise nonconforming identifiers cannot masquerade as the package's opaque trust-reference format. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. Content-bearing evidence adds an independent SHA-256 digest where integrity evidence is required. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose tenant, Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. @@ -14,7 +14,7 @@ The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan The packet always remains `requires_human_review`, requires explicit human confirmation, and fixes decision authority to `human_review_only`. Its analysis scope is the total selection process for one Job. It does not calculate selection rates, apply the four-fifths rule, estimate statistical significance, infer discrimination, or make an employment-process change. -Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary so a syntactically valid UUIDv4 reference from another tenant cannot be mixed into the monitoring envelope. It must specifically re-resolve `actor_reference` and `reviewer_reference` and prove their resolved actor identities are distinct, then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. UUIDv4 constrains public-identity opacity only; it does not establish tenant ownership, actor identity, or evidence validity. +Different requester/reviewer references are only an early syntactic guard. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary so a syntactically valid reference from another tenant cannot be mixed into the monitoring envelope. It must specifically re-resolve `actor_reference` and `reviewer_reference` and prove their resolved actor identities are distinct, then verify Job scope, aggregate population completeness, protected-attribute handling, small-sample policy, and statistical-plan provenance before routing the evidence to an authorized analyst and accountable human reviewer for any process change or legal conclusion. UUIDv4 constrains packet-owned trust-reference opacity only; it does not establish tenant ownership, actor identity, or evidence validity. Tenant UUID generation/privacy policy remains owned by the authoritative HRIS boundary. ## Example @@ -49,4 +49,4 @@ plan = build_selection_outcome_monitoring_plan( ) ``` -This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, authoritative tenant-scoped reference and actor resolution, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. \ No newline at end of file +This package writes no database tables and performs no cross-service SQL. A future persistence or analytics implementation must preserve purpose-bound authorization, authoritative tenant-scoped reference and actor resolution, aggregate-only/minimum-necessary access, small-sample controls, immutable audit evidence, and accountable human review independently. From efab4bccb478ab97bd09e7bdf375ff0275be57a6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:20:07 -0700 Subject: [PATCH 52/95] docs: separate monitoring tenant and packet UUID ownership --- .../0016-governed-selection-outcome-monitoring-plan.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md index 971aa9cb8..57d5c0f7a 100644 --- a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -5,7 +5,7 @@ ## Context -Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. Different opaque requester/reviewer references also do not prove that the authoritative actor boundary resolves them to different accountable people, and valid UUID-backed references alone do not prove that all referenced evidence belongs to the packet tenant. UUIDv1 additionally embeds timestamp/node-derived correlation metadata, making it unsuitable for a public tenant identity or a field presented as an opaque trust reference. +Orgmetra already owns Job-scoped selection and post-hire evidence boundaries, but protected `develop` does not define a buyer-facing contract for planning recurring selection-outcome monitoring without copying candidate-level protected-attribute values or turning a screening heuristic into an automated legal or employment decision. Different opaque requester/reviewer references also do not prove that the authoritative actor boundary resolves them to different accountable people, and valid UUID-backed references alone do not prove that all referenced evidence belongs to the packet tenant. Packet-owned UUIDv1 trust references additionally embed timestamp/node-derived correlation metadata. The authoritative tenant identifier is different: it is issued by Orgmetra core, so this leaf package must accept the canonical non-sentinel operational UUID contract owned by that boundary rather than silently imposing a second version policy. The EEOC's common interpretation of the Uniform Guidelines directs users to examine the total selection process first for each job, describes the four-fifths rule as a rule of thumb rather than a legal definition, and notes that small samples, statistical significance, practical significance, and other evidence can matter. ISO 30405:2023 also treats reviewing and learning as part of recruitment practice. SIOP's fifth-edition Principles provide the professional validation framework for personnel selection procedures. @@ -13,7 +13,7 @@ The EEOC's common interpretation of the Uniform Guidelines directs users to exam Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that binds: -- one canonical UUIDv4 operational tenant and authoritative Job; +- one canonical non-sentinel operational tenant under the authoritative Orgmetra core contract and one authoritative Job; - one total selection-process reference; - exact aggregate population and selection-outcome snapshot references and SHA-256 digests; - exact protected-attribute handling, small-sample interpretation, and statistical-analysis plan references and digests; @@ -21,7 +21,7 @@ Orgmetra will expose a transport-neutral `SelectionOutcomeMonitoringPlan` that b - fixed purpose and reviewed reason metadata plus a bounded positive `evidence_version` that is part of canonical evidence; - an explicit monitoring business-date window and evidence-generation instant. -The public `tenant_record_id` and every namespaced trust-bearing reference use canonical non-sentinel UUIDv4 identity; namespaced references additionally require their expected prefix. UUIDv1 and every other UUID version fail closed so timestamp/node-derived correlation metadata cannot enter public identity fields represented as opaque. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. `evidence_version` must be a true integer from 1 through 2147483647; changing it changes canonical JSON and the packet SHA-256, so revisions to actor/purpose/reason-bound evidence cannot silently collide. +`tenant_record_id` must be canonical and non-sentinel under Orgmetra's authoritative operational UUID contract. The package does not reinterpret its UUID version because tenant identity generation and migration policy belong to the authoritative HRIS boundary. Packet-owned namespaced trust-bearing references separately require canonical non-sentinel UUIDv4 plus their expected prefix. UUIDv1 and other non-v4 suffixes fail closed for those references. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so labels, policy values, protected-attribute concepts, or actor names cannot be carried through fields represented as opaque identifiers. `evidence_version` must be a true integer from 1 through 2147483647; changing it changes canonical JSON and the packet SHA-256, so revisions to actor/purpose/reason-bound evidence cannot silently collide. UUID syntax is not tenant authority. Before review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through the relevant authoritative boundary and reject review use if any reference belongs to another tenant or cannot be authoritatively resolved. The packet also rejects identical requester/reviewer references as an early syntactic guard; after tenant-scoped resolution, the host must prove that the two references resolve to distinct actor identities. Reference inequality alone is not separation-of-duties evidence. @@ -33,7 +33,7 @@ Any later analytics or persistence boundary must independently enforce purpose-b - Buyers obtain a deterministic, explicitly versioned governance envelope for recurring selection monitoring without creating a second psychometrics/statistics engine inside Orgmetra. - The total-process-by-Job scope is explicit before any future component drill-down. -- Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and public tenant/reference identities cannot carry UUIDv1 timestamp/node metadata or value-bearing suffixes. +- Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and packet-owned trust references reject UUIDv1 timestamp/node metadata and value-bearing suffixes without making the leaf package incompatible with authoritative Orgmetra tenant UUIDs. - Cross-tenant evidence mixing is fail-closed at the host review boundary because every opaque reference must be re-resolved in the exact packet tenant. - Requester/reviewer separation is proven from authoritative resolved actor identities rather than inferred from different opaque strings. - The four-fifths rule cannot be represented as an automatic pass/fail legal rule by this contract; interpretation remains with authorized analysts and accountable humans. @@ -41,4 +41,4 @@ Any later analytics or persistence boundary must independently enforce purpose-b ## References -See `docs/doctoring/selection-outcome-monitoring-references.md`. \ No newline at end of file +See `docs/doctoring/selection-outcome-monitoring-references.md`. From 6fffea3a21f6b22beddd5cd4fabacc471f080dcc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:20:27 -0700 Subject: [PATCH 53/95] docs: trace monitoring tenant UUID interoperability --- docs/traceability/selection-outcome-monitoring.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index 4e574508b..c5150cd15 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -8,10 +8,10 @@ | Buyer / governance need | Owned contract evidence | Explicit non-claim | |---|---|---| -| Monitor the correct hiring/promotion process | Canonical UUIDv4 `tenant_record_id`, exact UUIDv4-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job`; immutable next action requires every packet reference to be re-resolved within exact `tenant_record_id` | UUID syntax alone is not tenant authority or component-level causality evidence | +| Monitor the correct hiring/promotion process | Canonical non-sentinel `tenant_record_id` under the Orgmetra core operational-UUID contract, exact UUIDv4-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job`; immutable next action requires every packet reference to be re-resolved within exact `tenant_record_id` | UUID syntax alone is not tenant authority or component-level causality evidence | | Reproduce the monitored population and outcomes | Exact UUIDv4-backed aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | | Preserve privacy and interpretation rules | Exact UUIDv4-backed protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | -| Prevent semantic/value/correlation smuggling through public IDs | `tenant_record_id` and every governed reference require canonical non-sentinel UUIDv4 identity; namespaced references also require the expected prefix; `test_reference_privacy.py` covers UUIDv1 tenant identity plus value-bearing, sentinel, noncanonical and UUIDv1 reference cases through builder and `dataclasses.replace(...)` paths | UUIDv4 syntax does not prove source truth, tenant membership, or authorization | +| Prevent semantic/value/correlation smuggling through packet-owned references without duplicating tenant identity policy | `tenant_record_id` is canonical/non-sentinel under the authoritative core contract; every packet-owned governed reference requires canonical non-sentinel UUIDv4 plus its expected prefix; `test_reference_privacy.py` covers authoritative UUIDv7 tenant interoperability plus value-bearing, sentinel, noncanonical and UUIDv1 reference cases through builder and `dataclasses.replace(...)` paths | UUID syntax does not prove source truth, tenant membership, or authorization | | Prevent cross-tenant evidence mixing | `test_actor_separation.py` requires the governed next action to re-resolve every packet reference within `tenant_record_id` before actor separation, Job scope verification, or accountable review | The packet does not itself query authoritative stores | | Bind the analysis method before interpretation | Exact UUIDv4-backed statistical-plan reference/digest | No statistics are calculated by this package | | Version actor/purpose/reason evidence explicitly | `evidence_version` is a true positive integer through signed-int32 max and participates in canonical JSON/SHA-256 | `test_evidence_version.py` proves presence, digest separation, bounds, and `dataclasses.replace(...)` revalidation | @@ -21,10 +21,10 @@ ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy contract for rejecting UUIDv1 public tenant identity plus human-readable/value-bearing, sentinel, noncanonical, and non-v4 opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. ## Ownership boundary -This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Authoritative tenant-scoped reference and actor resolution remains at the host boundary; this packet fails closed by requiring that proof before human review use. \ No newline at end of file +This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Tenant UUID generation/privacy policy and authoritative tenant-scoped reference/actor resolution remain at the host/core boundary; this packet fails closed by requiring that proof before human review use. From acc3d3828a2d2624fb9311d65c36555a724692a2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:20:42 -0700 Subject: [PATCH 54/95] docs: record monitoring tenant identity interoperability repair --- packages/selection-monitoring/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 1a8ffae4a..6cdf36b82 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -5,6 +5,6 @@ All notable package changes are recorded here. ## Unreleased - Add a governed, aggregate-only `SelectionOutcomeMonitoringPlan` that binds one Job-scoped total selection process to exact aggregate population/outcome snapshots, protected-attribute handling, small-sample interpretation, statistical-plan provenance, a distinct accountable reviewer, and explicit human review without carrying candidate-level values or making an adverse-impact/legal determination. -- Require the public `tenant_record_id` and every namespaced trust-bearing reference to use canonical non-sentinel UUIDv4 identity, rejecting UUIDv1 timestamp/node correlation metadata as well as human-readable, value-bearing, sentinel, noncanonical, and other non-v4 reference suffixes through construction and replacement paths. +- Follow Orgmetra's authoritative canonical non-sentinel operational UUID contract for `tenant_record_id`, while every packet-owned namespaced trust-bearing reference remains canonical non-sentinel UUIDv4 and rejects UUIDv1/non-v4, human-readable, value-bearing, sentinel, and noncanonical suffixes through construction and replacement paths. - Require every packet reference to be re-resolved within the exact tenant through its authoritative boundary before actor separation, Job-scope verification, or accountable review, preventing cross-tenant evidence mixing behind valid opaque UUIDs. -- Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. \ No newline at end of file +- Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. From a306d8199be475ba07682e31c1f9b4d9c701a50b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 17:11:45 -0700 Subject: [PATCH 55/95] test(selection-monitoring): cover direct reference construction --- .../tests/test_reference_privacy.py | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/selection-monitoring/tests/test_reference_privacy.py b/packages/selection-monitoring/tests/test_reference_privacy.py index b3b8afd22..bb9bdf821 100644 --- a/packages/selection-monitoring/tests/test_reference_privacy.py +++ b/packages/selection-monitoring/tests/test_reference_privacy.py @@ -7,15 +7,18 @@ import pytest -from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan +from orgmetra_selection_monitoring import ( + SelectionOutcomeMonitoringPlan, + build_selection_outcome_monitoring_plan, +) UUID1_ID = "6ba7b810-9dad-11d1-80b4-00c04fd430c8" UUID7_TENANT = "10000000-0000-7000-8000-000000000001" -def _build(**overrides): - """Build a valid packet using canonical UUIDv4-backed opaque references.""" - values = { +def _valid_values(**overrides) -> dict[str, object]: + """Return valid constructor values for every governed monitoring field.""" + values: dict[str, object] = { "tenant_record_id": "11111111-1111-4111-8111-111111111111", "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", "job_profile_reference": "job_profile:10000000-0000-4000-8000-000000000002", @@ -39,16 +42,28 @@ def _build(**overrides): "generated_at": datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc), } values.update(overrides) - return build_selection_outcome_monitoring_plan(**values) + return values + + +def _build(**overrides) -> SelectionOutcomeMonitoringPlan: + """Build a valid packet through the public builder.""" + return build_selection_outcome_monitoring_plan(**_valid_values(**overrides)) # type: ignore[arg-type] + + +def _direct(**overrides) -> SelectionOutcomeMonitoringPlan: + """Construct a packet directly to prove dataclass invariants cannot be bypassed.""" + return SelectionOutcomeMonitoringPlan(**_valid_values(**overrides)) # type: ignore[arg-type] -def test_authoritative_uuid7_tenant_identity_is_accepted_by_builder_and_replace() -> None: +def test_authoritative_uuid7_tenant_identity_is_accepted_by_all_construction_paths() -> None: """The monitoring leaf must accept tenant UUIDs already valid in authoritative core.""" packet = _build(tenant_record_id=UUID7_TENANT) replaced = replace(_build(), tenant_record_id=UUID7_TENANT) + direct = _direct(tenant_record_id=UUID7_TENANT) assert packet.tenant_record_id == UUID7_TENANT assert replaced.tenant_record_id == UUID7_TENANT + assert direct.tenant_record_id == UUID7_TENANT @pytest.mark.parametrize( @@ -88,7 +103,7 @@ def test_references_reject_value_bearing_sentinel_and_noncanonical_suffixes( value: object, message: str, ) -> None: - """Reject reference suffixes that can leak values or evade opaque-ID rules.""" + """Reject unsafe reference suffixes through builder, replace, and direct construction.""" with pytest.raises(ValueError, match=message): _build(**{field_name: value}) @@ -96,6 +111,9 @@ def test_references_reject_value_bearing_sentinel_and_noncanonical_suffixes( with pytest.raises(ValueError, match=message): replace(packet, **{field_name: value}) + with pytest.raises(ValueError, match=message): + _direct(**{field_name: value}) + @pytest.mark.parametrize( ("field_name", "prefix"), @@ -112,7 +130,7 @@ def test_references_reject_value_bearing_sentinel_and_noncanonical_suffixes( ("reviewer_reference", "actor"), ], ) -def test_uuid1_trust_reference_is_rejected_by_builder_and_replace( +def test_uuid1_trust_reference_is_rejected_by_all_construction_paths( field_name: str, prefix: str, ) -> None: @@ -123,3 +141,6 @@ def test_uuid1_trust_reference_is_rejected_by_builder_and_replace( with pytest.raises(ValueError, match=field_name): replace(_build(), **{field_name: value}) + + with pytest.raises(ValueError, match=field_name): + _direct(**{field_name: value}) From d907600ea75102e64b69f4e7f14ed8b935e42e0b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 21:02:42 -0700 Subject: [PATCH 56/95] test(selection-monitoring): reject temporal evidence subclasses --- .../tests/test_temporal_evidence_integrity.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_temporal_evidence_integrity.py diff --git a/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py new file mode 100644 index 000000000..c9f508a13 --- /dev/null +++ b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py @@ -0,0 +1,75 @@ +"""Regression tests for exact temporal types in immutable monitoring evidence.""" + +from __future__ import annotations + +from datetime import date, datetime, timezone + +import pytest + +from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan + + +class ForgedDate(date): + """Date subclass able to forge the canonical evidence rendering.""" + + def isoformat(self) -> str: + """Return a date different from the underlying business date.""" + return "2099-12-31" + + +class ForgedDateTime(datetime): + """Datetime subclass able to forge the canonical evidence rendering.""" + + def astimezone(self, tz=None): # type: ignore[no-untyped-def] + """Keep the subclass alive across the UTC normalization call.""" + return self + + def isoformat(self, *args, **kwargs) -> str: # type: ignore[no-untyped-def] + """Return an instant different from the underlying evidence instant.""" + return "2099-12-31T23:59:59+00:00" + + +def valid_kwargs() -> dict[str, object]: + """Return one otherwise valid aggregate-monitoring plan input.""" + return { + "tenant_record_id": "11111111-1111-4111-8111-111111111111", + "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + "job_profile_reference": "job_profile:10000000-0000-4000-8000-000000000002", + "selection_process_reference": "selection_process:10000000-0000-4000-8000-000000000003", + "population_snapshot_reference": "population_snapshot:10000000-0000-4000-8000-000000000004", + "population_snapshot_digest": "a" * 64, + "outcome_snapshot_reference": "selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", + "outcome_snapshot_digest": "b" * 64, + "protected_attribute_policy_reference": "protected_attribute_policy:10000000-0000-4000-8000-000000000006", + "protected_attribute_policy_digest": "c" * 64, + "small_sample_policy_reference": "small_sample_policy:10000000-0000-4000-8000-000000000007", + "small_sample_policy_digest": "d" * 64, + "statistical_plan_reference": "statistical_plan:10000000-0000-4000-8000-000000000008", + "statistical_plan_digest": "e" * 64, + "actor_reference": "actor:10000000-0000-4000-8000-000000000009", + "reviewer_reference": "actor:10000000-0000-4000-8000-00000000000a", + "monitoring_start": date(2026, 1, 1), + "monitoring_end": date(2026, 3, 31), + "purpose_code": "selection_outcome_monitoring", + "reason_code": "quarterly_selection_governance", + "generated_at": datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc), + } + + +@pytest.mark.parametrize("field_name", ["monitoring_start", "monitoring_end"]) +def test_rejects_date_subclasses_that_can_forge_canonical_business_time(field_name: str) -> None: + """Do not let subclass methods rewrite immutable business-time evidence.""" + kwargs = valid_kwargs() + kwargs[field_name] = ForgedDate(2026, 1, 1 if field_name == "monitoring_start" else 3) + + with pytest.raises(ValueError, match="calendar date"): + build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_datetime_subclasses_that_can_forge_canonical_recorded_time() -> None: + """Do not let subclass methods rewrite immutable recorded-time evidence.""" + kwargs = valid_kwargs() + kwargs["generated_at"] = ForgedDateTime(2026, 4, 2, 8, 30, tzinfo=timezone.utc) + + with pytest.raises(ValueError, match="timezone-aware"): + build_selection_outcome_monitoring_plan(**kwargs) From 200c4c5508253eb3e3e1f5e5f13db70de30ae328 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 21:03:30 -0700 Subject: [PATCH 57/95] fix(selection-monitoring): require exact temporal evidence types --- .../src/orgmetra_selection_monitoring/plan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 199c93815..d2a890bff 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -78,8 +78,8 @@ def _validate_digest(value: str, field_name: str) -> None: def _canonical_timestamp(value: datetime) -> str: """Render an aware instant as precision-preserving UTC RFC 3339 text.""" - if not isinstance(value, datetime) or value.tzinfo is None or value.utcoffset() is None: - raise ValueError("generated_at must be timezone-aware") + if type(value) is not datetime or value.tzinfo is None or value.utcoffset() is None: + raise ValueError("generated_at must be an exact timezone-aware datetime") return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") @@ -167,9 +167,9 @@ def __post_init__(self) -> None: _validate_reference(self.reviewer_reference, "actor", "reviewer_reference") if self.actor_reference == self.reviewer_reference: raise ValueError("reviewer_reference must identify a different accountable actor") - if not isinstance(self.monitoring_start, date) or isinstance(self.monitoring_start, datetime): + if type(self.monitoring_start) is not date: raise ValueError("monitoring_start must be a calendar date") - if not isinstance(self.monitoring_end, date) or isinstance(self.monitoring_end, datetime): + if type(self.monitoring_end) is not date: raise ValueError("monitoring_end must be a calendar date") if self.monitoring_end < self.monitoring_start: raise ValueError("monitoring_end must not precede monitoring_start") From 5c40ca0c4dd56c03db726c36cd2c7093bb9e3fa7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 23:05:01 -0700 Subject: [PATCH 58/95] test(selection-monitoring): reject forged string evidence types --- .../test_string_runtime_evidence_integrity.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py diff --git a/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py b/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py new file mode 100644 index 000000000..618d7be7a --- /dev/null +++ b/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py @@ -0,0 +1,87 @@ +"""Regression coverage for string-subclass evidence-boundary integrity.""" + +from __future__ import annotations + +from datetime import date, datetime, timezone + +import pytest + +from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan + + +class ForgedReference(str): + """String subclass that forges namespace and UUID suffix validation.""" + + def startswith(self, prefix, *args): # type: ignore[no-untyped-def] + """Pretend the hostile value carries every requested namespace.""" + return True + + def split(self, sep=None, maxsplit=-1): # type: ignore[no-untyped-def] + """Feed validation a canonical UUIDv4 suffix instead of stored text.""" + return ["evil", "11111111-1111-4111-8111-111111111111"] + + +class ForgedTenantUUIDText(str): + """String subclass that forges UUID parsing and canonical-equality checks.""" + + def replace(self, old, new, *args): # type: ignore[no-untyped-def] + """Feed UUID() canonical text instead of the stored hostile tenant text.""" + canonical = "12345678-1234-4234-8234-123456789abc" + return canonical.replace(old, new, *args) + + def __eq__(self, other): # type: ignore[no-untyped-def] + """Claim canonical equality while retaining the hostile underlying text.""" + if other is None: + return False + return True + + def __ne__(self, other): # type: ignore[no-untyped-def] + """Keep UUID constructor sentinel checks working while defeating canonicality.""" + if other is None: + return True + return False + + +def valid_kwargs() -> dict[str, object]: + """Return one otherwise valid monitoring-plan input.""" + return { + "tenant_record_id": "11111111-1111-4111-8111-111111111111", + "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + "job_profile_reference": "job_profile:10000000-0000-4000-8000-000000000002", + "selection_process_reference": "selection_process:10000000-0000-4000-8000-000000000003", + "population_snapshot_reference": "population_snapshot:10000000-0000-4000-8000-000000000004", + "population_snapshot_digest": "a" * 64, + "outcome_snapshot_reference": "selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", + "outcome_snapshot_digest": "b" * 64, + "protected_attribute_policy_reference": "protected_attribute_policy:10000000-0000-4000-8000-000000000006", + "protected_attribute_policy_digest": "c" * 64, + "small_sample_policy_reference": "small_sample_policy:10000000-0000-4000-8000-000000000007", + "small_sample_policy_digest": "d" * 64, + "statistical_plan_reference": "statistical_plan:10000000-0000-4000-8000-000000000008", + "statistical_plan_digest": "e" * 64, + "actor_reference": "actor:10000000-0000-4000-8000-000000000009", + "reviewer_reference": "actor:10000000-0000-4000-8000-00000000000a", + "monitoring_start": date(2026, 1, 1), + "monitoring_end": date(2026, 3, 31), + "purpose_code": "selection_outcome_monitoring", + "reason_code": "quarterly_selection_governance", + "generated_at": datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc), + } + + +def test_rejects_reference_string_subclass_that_can_forge_namespace_validation() -> None: + """Canonical evidence must not retain text that only pretended to match a namespace.""" + kwargs = valid_kwargs() + kwargs["monitoring_plan_reference"] = ForgedReference("evil:payload") + + with pytest.raises(ValueError, match="monitoring_plan_reference"): + build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_tenant_string_subclass_that_can_forge_uuid_validation() -> None: + """Authoritative tenant identity must be exact built-in text before UUID parsing.""" + kwargs = valid_kwargs() + kwargs["tenant_record_id"] = ForgedTenantUUIDText("not-a-tenant-uuid") + + with pytest.raises(ValueError, match="tenant_record_id"): + build_selection_outcome_monitoring_plan(**kwargs) From 3c581c5730f26b26aaf7138e0879f6a60cd99a9d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 23:05:32 -0700 Subject: [PATCH 59/95] fix(selection-monitoring): require exact string evidence types --- .../src/orgmetra_selection_monitoring/plan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index d2a890bff..ed4fa7aa0 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -37,6 +37,8 @@ def _validate_operational_uuid(value: str, field_name: str) -> None: """Require canonical non-sentinel UUID text owned by the authoritative HRIS.""" + if type(value) is not str: + raise ValueError(f"{field_name} must be canonical UUID text") try: parsed = UUID(value) except (ValueError, AttributeError, TypeError) as exc: @@ -55,7 +57,7 @@ def _validate_reference(value: str, prefix: str, field_name: str) -> None: """Require an expected namespace plus a canonical opaque UUIDv4 suffix.""" error_message = f"{field_name} must be an opaque {prefix}: reference" if ( - not isinstance(value, str) + type(value) is not str or len(value) > 160 or not _REFERENCE_PATTERN.fullmatch(value) or not value.startswith(f"{prefix}:") From cdbc2c0b9b1a3c53462ab2408469deec271b07eb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 23:17:56 -0700 Subject: [PATCH 60/95] test(selection-monitoring): reject forged governance codes --- .../test_string_runtime_evidence_integrity.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py b/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py index 618d7be7a..f543812b8 100644 --- a/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py +++ b/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py @@ -42,6 +42,19 @@ def __ne__(self, other): # type: ignore[no-untyped-def] return False +class ForgedGovernanceCode(str): + """String subclass that can satisfy closed-code comparisons with hostile text.""" + + def __eq__(self, other): # type: ignore[no-untyped-def] + return True + + def __ne__(self, other): # type: ignore[no-untyped-def] + return False + + def __hash__(self) -> int: + return hash("quarterly_selection_governance") + + def valid_kwargs() -> dict[str, object]: """Return one otherwise valid monitoring-plan input.""" return { @@ -85,3 +98,21 @@ def test_rejects_tenant_string_subclass_that_can_forge_uuid_validation() -> None with pytest.raises(ValueError, match="tenant_record_id"): build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_purpose_code_string_subclass_that_can_forge_closed_code_check() -> None: + """Purpose evidence must be exact built-in text before fixed-code comparison.""" + kwargs = valid_kwargs() + kwargs["purpose_code"] = ForgedGovernanceCode("attacker_controlled_purpose") + + with pytest.raises(ValueError, match="purpose_code"): + build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_reason_code_string_subclass_that_can_forge_closed_code_membership() -> None: + """Reason evidence must be exact built-in text before allow-list membership.""" + kwargs = valid_kwargs() + kwargs["reason_code"] = ForgedGovernanceCode("attacker_controlled_reason") + + with pytest.raises(ValueError, match="reason_code"): + build_selection_outcome_monitoring_plan(**kwargs) From 87a66338b60af72d10ade79a34a009415dbefdc2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 23:18:34 -0700 Subject: [PATCH 61/95] fix(selection-monitoring): require exact governance-code text --- .../src/orgmetra_selection_monitoring/plan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index ed4fa7aa0..1928a7986 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -48,8 +48,8 @@ def _validate_operational_uuid(value: str, field_name: str) -> None: def _validate_code(value: str, field_name: str) -> None: - """Require a bounded descriptive lower snake_case governance code.""" - if not isinstance(value, str) or len(value) > 64 or not _CODE_PATTERN.fullmatch(value): + """Require exact bounded descriptive lower snake_case governance text.""" + if type(value) is not str or len(value) > 64 or not _CODE_PATTERN.fullmatch(value): raise ValueError(f"{field_name} must be bounded two-or-more-word lower snake_case") From f31894424e0c675b996636d5611c09a758f55862 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 00:01:45 -0700 Subject: [PATCH 62/95] test(selection-monitoring): reject forged fixed governance text --- ...test_fixed_governance_runtime_integrity.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py diff --git a/packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py b/packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py new file mode 100644 index 000000000..13732fdeb --- /dev/null +++ b/packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py @@ -0,0 +1,32 @@ +"""Regression tests for fixed selection-monitoring governance text integrity.""" + +from dataclasses import replace + +import pytest + +from test_plan import build_valid + + +class ForgedFixedGovernanceText(str): + """String subclass that lies during equality checks but keeps forged JSON text.""" + + def __eq__(self, other: object) -> bool: + """Pretend the forged value equals every governed constant.""" + return True + + def __ne__(self, other: object) -> bool: + """Pretend the forged value never differs from a governed constant.""" + return False + + +@pytest.mark.parametrize( + "field_name", + ["analysis_scope", "decision_authority", "review_state", "next_action"], +) +def test_rejects_string_subclasses_for_fixed_governance_fields(field_name: str) -> None: + """Reject canonical evidence whose fixed governance text can bypass comparison.""" + with pytest.raises(ValueError, match=field_name): + replace( + build_valid(), + **{field_name: ForgedFixedGovernanceText("forged_governance_value")}, + ) From 8a0b111d8b0c7f0dafe0e444c073b24903a3bfa3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 00:02:28 -0700 Subject: [PATCH 63/95] fix(selection-monitoring): protect fixed governance runtime types --- .../src/orgmetra_selection_monitoring/plan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 1928a7986..b06b54a5a 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -184,17 +184,17 @@ def __post_init__(self) -> None: _canonical_timestamp(self.generated_at) if type(self.evidence_version) is not int or not 1 <= self.evidence_version <= 2_147_483_647: raise ValueError("evidence_version must be an integer from 1 through 2147483647") - if self.analysis_scope != _ANALYSIS_SCOPE: + if type(self.analysis_scope) is not str or self.analysis_scope != _ANALYSIS_SCOPE: raise ValueError("analysis_scope must remain total_selection_process_by_job") if self.contains_individual_records is not False: raise ValueError("monitoring plan must remain aggregate-only") if self.human_confirmation_required is not True: raise ValueError("human confirmation is mandatory before monitoring use") - if self.decision_authority != _DECISION_AUTHORITY: + if type(self.decision_authority) is not str or self.decision_authority != _DECISION_AUTHORITY: raise ValueError("decision_authority must remain human_review_only") - if self.review_state != _REVIEW_STATE: + if type(self.review_state) is not str or self.review_state != _REVIEW_STATE: raise ValueError("review_state must remain requires_human_review") - if self.next_action != _NEXT_ACTION: + if type(self.next_action) is not str or self.next_action != _NEXT_ACTION: raise ValueError("next_action must remain the governed monitoring instruction") def __repr__(self) -> str: From 0cb4eda7b9ce39fccf9345ae26ec43548ef1b205 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 01:11:19 -0700 Subject: [PATCH 64/95] test(selection-monitoring): detach mutable generated-time timezone --- .../tests/test_temporal_evidence_integrity.py | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py index c9f508a13..5534a0a52 100644 --- a/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py +++ b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py @@ -2,7 +2,7 @@ from __future__ import annotations -from datetime import date, datetime, timezone +from datetime import date, datetime, timedelta, timezone, tzinfo import pytest @@ -29,6 +29,26 @@ def isoformat(self, *args, **kwargs) -> str: # type: ignore[no-untyped-def] return "2099-12-31T23:59:59+00:00" +class MutableTimezone(tzinfo): + """Timezone provider whose offset can change after packet issuance.""" + + def __init__(self, offset: timedelta) -> None: + """Store one caller-controlled offset.""" + self.offset = offset + + def utcoffset(self, dt: datetime | None) -> timedelta: + """Return the current mutable offset.""" + return self.offset + + def dst(self, dt: datetime | None) -> timedelta: + """Expose no daylight-saving adjustment.""" + return timedelta(0) + + def tzname(self, dt: datetime | None) -> str: + """Return a stable diagnostic timezone name.""" + return "MutableTimezone" + + def valid_kwargs() -> dict[str, object]: """Return one otherwise valid aggregate-monitoring plan input.""" return { @@ -73,3 +93,18 @@ def test_rejects_datetime_subclasses_that_can_forge_canonical_recorded_time() -> with pytest.raises(ValueError, match="timezone-aware"): build_selection_outcome_monitoring_plan(**kwargs) + + +def test_detaches_mutable_timezone_from_immutable_generated_time() -> None: + """Do not let a timezone provider rewrite canonical evidence after issuance.""" + provider = MutableTimezone(timedelta(hours=9)) + kwargs = valid_kwargs() + kwargs["generated_at"] = datetime(2026, 4, 2, 17, 30, tzinfo=provider) + + plan = build_selection_outcome_monitoring_plan(**kwargs) + before = plan.canonical_json() + provider.offset = timedelta(hours=-7) + + assert plan.canonical_json() == before + assert plan.generated_at == datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc) + assert plan.generated_at.tzinfo is timezone.utc From f4c38d6f51beb39d977e38f4d35caeb9b4b985ca Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 01:12:14 -0700 Subject: [PATCH 65/95] test(selection-monitoring): pin generation-time issuance integrity --- .../tests/test_temporal_evidence_integrity.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py index 5534a0a52..b43cd7572 100644 --- a/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py +++ b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py @@ -49,6 +49,18 @@ def tzname(self, dt: datetime | None) -> str: return "MutableTimezone" +class RaisingTimezone(tzinfo): + """Timezone provider that raises while resolving its UTC offset.""" + + def utcoffset(self, dt: datetime | None) -> timedelta: + """Raise caller-controlled behavior at the trust boundary.""" + raise RuntimeError("provider failure") + + def dst(self, dt: datetime | None) -> timedelta: + """Expose no daylight-saving adjustment.""" + return timedelta(0) + + def valid_kwargs() -> dict[str, object]: """Return one otherwise valid aggregate-monitoring plan input.""" return { @@ -108,3 +120,21 @@ def test_detaches_mutable_timezone_from_immutable_generated_time() -> None: assert plan.canonical_json() == before assert plan.generated_at == datetime(2026, 4, 2, 8, 30, tzinfo=timezone.utc) assert plan.generated_at.tzinfo is timezone.utc + + +def test_rejects_future_generated_time() -> None: + """Do not seal a monitoring plan for a system time that has not occurred.""" + kwargs = valid_kwargs() + kwargs["generated_at"] = datetime(2099, 1, 1, tzinfo=timezone.utc) + + with pytest.raises(ValueError, match="generated_at must not be in the future"): + build_selection_outcome_monitoring_plan(**kwargs) + + +def test_normalizes_timezone_provider_failure() -> None: + """Do not leak arbitrary timezone-provider exceptions across the evidence boundary.""" + kwargs = valid_kwargs() + kwargs["generated_at"] = datetime(2026, 4, 2, 8, 30, tzinfo=RaisingTimezone()) + + with pytest.raises(ValueError, match="timezone-aware"): + build_selection_outcome_monitoring_plan(**kwargs) From a976c9122741188c62c65a7b604e22519cc03661 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 01:12:56 -0700 Subject: [PATCH 66/95] fix(selection-monitoring): freeze generated-time evidence --- .../src/orgmetra_selection_monitoring/plan.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index b06b54a5a..a8acac781 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -8,7 +8,7 @@ from __future__ import annotations from dataclasses import dataclass -from datetime import date, datetime, timezone +from datetime import date, datetime, timedelta, timezone from hashlib import sha256 import json import re @@ -78,11 +78,30 @@ def _validate_digest(value: str, field_name: str) -> None: raise ValueError(f"{field_name} must be lowercase SHA-256 hex") +def _freeze_timestamp(value: datetime) -> datetime: + """Resolve caller timezone behavior once and store one immutable UTC instant.""" + if type(value) is not datetime or value.tzinfo is None: + raise ValueError("generated_at must be an exact timezone-aware datetime") + try: + offset = value.utcoffset() + except Exception as exc: + raise ValueError("generated_at must be an exact timezone-aware datetime") from exc + if type(offset) is not timedelta: + raise ValueError("generated_at must be an exact timezone-aware datetime") + try: + frozen = (value.replace(tzinfo=None) - offset).replace(tzinfo=timezone.utc) + except (OverflowError, ValueError) as exc: + raise ValueError("generated_at must be an exact timezone-aware datetime") from exc + if frozen > datetime.now(timezone.utc): + raise ValueError("generated_at must not be in the future") + return frozen + + def _canonical_timestamp(value: datetime) -> str: - """Render an aware instant as precision-preserving UTC RFC 3339 text.""" - if type(value) is not datetime or value.tzinfo is None or value.utcoffset() is None: + """Render one already-frozen UTC instant as precision-preserving RFC 3339 text.""" + if type(value) is not datetime or value.tzinfo is not timezone.utc: raise ValueError("generated_at must be an exact timezone-aware datetime") - return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + return value.isoformat().replace("+00:00", "Z") @dataclass(frozen=True, slots=True, repr=False) @@ -181,7 +200,7 @@ def __post_init__(self) -> None: _validate_code(self.reason_code, "reason_code") if self.reason_code not in _ALLOWED_REASON_CODES: raise ValueError("reason_code must use a reviewed non-sensitive monitoring reason") - _canonical_timestamp(self.generated_at) + object.__setattr__(self, "generated_at", _freeze_timestamp(self.generated_at)) if type(self.evidence_version) is not int or not 1 <= self.evidence_version <= 2_147_483_647: raise ValueError("evidence_version must be an integer from 1 through 2147483647") if type(self.analysis_scope) is not str or self.analysis_scope != _ANALYSIS_SCOPE: From c130984892dbcf03f0adbdec08c69e1daa4f9f3a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 01:13:22 -0700 Subject: [PATCH 67/95] test(selection-monitoring): cover frozen-time fail-closed paths --- .../tests/test_temporal_evidence_integrity.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py index b43cd7572..236643080 100644 --- a/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py +++ b/packages/selection-monitoring/tests/test_temporal_evidence_integrity.py @@ -138,3 +138,25 @@ def test_normalizes_timezone_provider_failure() -> None: with pytest.raises(ValueError, match="timezone-aware"): build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_timezone_normalization_overflow() -> None: + """Fail closed when a valid offset cannot be represented as a UTC datetime.""" + kwargs = valid_kwargs() + kwargs["generated_at"] = datetime.min.replace(tzinfo=timezone(timedelta(hours=14))) + + with pytest.raises(ValueError, match="timezone-aware"): + build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_post_construction_timezone_reinjection() -> None: + """Do not emit evidence after low-level replacement of the frozen UTC instant.""" + plan = build_selection_outcome_monitoring_plan(**valid_kwargs()) + object.__setattr__( + plan, + "generated_at", + datetime(2026, 4, 2, 17, 30, tzinfo=timezone(timedelta(hours=9))), + ) + + with pytest.raises(ValueError, match="timezone-aware"): + plan.canonical_json() From 4f2ac3836dfc650d3a887eebd35a61845570f4e2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 01:16:05 -0700 Subject: [PATCH 68/95] docs(selection-monitoring): record generation-time integrity repair --- packages/selection-monitoring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 6cdf36b82..7eaff7ac5 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -8,3 +8,4 @@ All notable package changes are recorded here. - Follow Orgmetra's authoritative canonical non-sentinel operational UUID contract for `tenant_record_id`, while every packet-owned namespaced trust-bearing reference remains canonical non-sentinel UUIDv4 and rejects UUIDv1/non-v4, human-readable, value-bearing, sentinel, and noncanonical suffixes through construction and replacement paths. - Require every packet reference to be re-resolved within the exact tenant through its authoritative boundary before actor separation, Job-scope verification, or accountable review, preventing cross-tenant evidence mixing behind valid opaque UUIDs. - Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. +- Freeze `generated_at` to a detached built-in UTC instant at issuance, reject future generation times, normalize caller timezone-provider failures to fail-closed validation errors, and prevent later mutable `tzinfo` behavior from rewriting canonical monitoring evidence. From eb3b12be1bc231868fc338e082ebd3a51cefb476 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 01:16:21 -0700 Subject: [PATCH 69/95] docs(selection-monitoring): explain frozen generation time --- packages/selection-monitoring/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 521ace5aa..c49f8b0aa 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -4,10 +4,12 @@ ## What the contract binds -A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, an explicit monitoring window, and a bounded positive `evidence_version`. +A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the total selection process being monitored, an aggregate population snapshot, an aggregate selection-outcome snapshot, the protected-attribute handling policy, small-sample interpretation policy, statistical analysis plan, accountable requester and reviewer references, an explicit monitoring window, a system-recorded generation instant, and a bounded positive `evidence_version`. `tenant_record_id` follows Orgmetra's authoritative canonical non-sentinel operational UUID contract instead of imposing a second UUID-version policy at this leaf package. Packet-owned trust-bearing artifacts remain canonical non-sentinel UUIDv4 identities; namespaced artifact references additionally require their expected namespace. UUIDv1 and other non-v4 suffixes are rejected for those references so timestamp/node-derived or otherwise nonconforming identifiers cannot masquerade as the package's opaque trust-reference format. Human-readable, value-bearing, sentinel, and noncanonical reference suffixes are also rejected so Job labels, policy values, protected-attribute concepts, actor names, or other sensitive semantics cannot be smuggled through a field described as opaque. Content-bearing evidence adds an independent SHA-256 digest where integrity evidence is required. `reason_code` is closed to the reviewed non-sensitive `quarterly_selection_governance` value for this initial contract, rather than accepting arbitrary lower-snake-case text. `evidence_version` must be a true integer from 1 through 2147483647 and participates in canonical JSON and SHA-256 evidence, so revisions to the actor/purpose/reason-bound monitoring evidence cannot silently collide. Canonical JSON and a packet digest support immutable audit correlation without copying candidate identities, protected-attribute values, assessment scores, individual decisions, or free-form model output. +`generated_at` is issuance-time evidence rather than a caller-controlled timezone object retained for later execution. Construction requires an exact built-in `datetime`, resolves any concrete `tzinfo` offset once, converts the result to a built-in UTC `datetime`, rejects future instants, and stores only that detached UTC instant. Later canonical export never invokes the caller's original timezone provider, so a mutable or stateful `tzinfo` cannot rewrite already-issued evidence. Provider exceptions, missing concrete offsets, and UTC-normalization overflow fail closed as `ValueError`; low-level reinjection of a non-UTC timestamp also fails before evidence emission. + The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose tenant, Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. ## Governance boundary From 9b871a3245671f0d14ea56e103ac0d9b91482d43 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 01:16:34 -0700 Subject: [PATCH 70/95] docs(selection-monitoring): trace generation-time integrity --- docs/traceability/selection-outcome-monitoring.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index c5150cd15..ad81d2fdf 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -17,11 +17,11 @@ | Version actor/purpose/reason evidence explicitly | `evidence_version` is a true positive integer through signed-int32 max and participates in canonical JSON/SHA-256 | `test_evidence_version.py` proves presence, digest separation, bounds, and `dataclasses.replace(...)` revalidation | | Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | -| Preserve replayable audit correlation | Precision-preserving UTC generation time, canonical JSON, SHA-256 packet digest | Digest proves envelope integrity, not source truth or scientific/legal validity | +| Preserve replayable audit correlation without caller-owned timezone behavior | `generated_at` is resolved once to a built-in UTC instant at issuance; future instants, missing/raising offsets and normalization overflow fail closed; canonical JSON and SHA-256 reuse only the detached UTC value | The packet timestamp proves evidence chronology/correlation, not source truth or scientific/legal validity | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. From 769bca8c7a17dfedf758ea980d0f0268e7b4b131 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:07:03 -0700 Subject: [PATCH 71/95] test(selection-monitoring): reject post-issuance evidence rewrites --- .../tests/test_issuance_integrity.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_issuance_integrity.py diff --git a/packages/selection-monitoring/tests/test_issuance_integrity.py b/packages/selection-monitoring/tests/test_issuance_integrity.py new file mode 100644 index 000000000..7dc71f06d --- /dev/null +++ b/packages/selection-monitoring/tests/test_issuance_integrity.py @@ -0,0 +1,63 @@ +"""Regression tests for immutable selection-monitoring issuance evidence.""" + +from __future__ import annotations + +from datetime import date, datetime, timezone + +import pytest + +from orgmetra_selection_monitoring import build_selection_outcome_monitoring_plan +from orgmetra_selection_monitoring import plan as plan_module + + +_BASE_KWARGS: dict[str, object] = { + "tenant_record_id": "11111111-1111-4111-8111-111111111111", + "monitoring_plan_reference": "selection_monitoring_plan:10000000-0000-4000-8000-000000000001", + "job_profile_reference": "job_profile:10000000-0000-4000-8000-000000000002", + "selection_process_reference": "selection_process:10000000-0000-4000-8000-000000000003", + "population_snapshot_reference": "population_snapshot:10000000-0000-4000-8000-000000000004", + "population_snapshot_digest": "a" * 64, + "outcome_snapshot_reference": "selection_outcome_snapshot:10000000-0000-4000-8000-000000000005", + "outcome_snapshot_digest": "b" * 64, + "protected_attribute_policy_reference": "protected_attribute_policy:10000000-0000-4000-8000-000000000006", + "protected_attribute_policy_digest": "c" * 64, + "small_sample_policy_reference": "small_sample_policy:10000000-0000-4000-8000-000000000007", + "small_sample_policy_digest": "d" * 64, + "statistical_plan_reference": "statistical_plan:10000000-0000-4000-8000-000000000008", + "statistical_plan_digest": "e" * 64, + "actor_reference": "actor:10000000-0000-4000-8000-000000000009", + "reviewer_reference": "actor:10000000-0000-4000-8000-00000000000a", + "monitoring_start": date(2026, 1, 1), + "monitoring_end": date(2026, 3, 31), + "purpose_code": "selection_outcome_monitoring", + "reason_code": "quarterly_selection_governance", + "generated_at": datetime(2026, 4, 2, 8, 30, 0, 123456, tzinfo=timezone.utc), +} + + +def _build_plan(): + """Build one valid issued monitoring plan for tamper-evidence regressions.""" + return build_selection_outcome_monitoring_plan(**_BASE_KWARGS) + + +def test_post_issuance_rewrite_cannot_change_canonical_evidence() -> None: + """Reject a valid-value rewrite after the governed plan has been issued.""" + plan = _build_plan() + original = plan.canonical_json() + + object.__setattr__(plan, "population_snapshot_digest", "f" * 64) + + with pytest.raises(ValueError, match="changed after issuance"): + plan.canonical_json() + with pytest.raises(ValueError, match="changed after issuance"): + plan.sha256_digest() + assert original != plan_module._canonical_plan_json_unchecked(plan) + + +def test_missing_process_local_issuance_evidence_fails_closed() -> None: + """Reject canonical export when process-local issuance evidence is unavailable.""" + plan = _build_plan() + plan_module._discard_plan_seal(id(plan)) + + with pytest.raises(ValueError, match="issuance evidence is unavailable"): + plan.canonical_json() From 26fa602da67e497f63b500d5411393547ca3d2e3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:09:50 -0700 Subject: [PATCH 72/95] fix(selection-monitoring): seal issued canonical evidence --- .../src/orgmetra_selection_monitoring/plan.py | 112 ++++++++++++------ 1 file changed, 78 insertions(+), 34 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index a8acac781..c28f863d3 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -10,9 +10,13 @@ from dataclasses import dataclass from datetime import date, datetime, timedelta, timezone from hashlib import sha256 +import hmac import json import re +import secrets +from threading import RLock from uuid import UUID +from weakref import finalize _CODE_PATTERN = re.compile(r"^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$") _DIGEST_PATTERN = re.compile(r"^[0-9a-f]{64}$") @@ -33,6 +37,34 @@ "analyst and accountable human reviewer for any employment-process change or legal " "conclusion." ) +_PROCESS_PLAN_SEAL_KEY = secrets.token_bytes(32) +_PLAN_SEALS: dict[int, str] = {} +_PLAN_SEALS_LOCK = RLock() + + +def _discard_plan_seal(plan_id: int) -> None: + """Discard process-local issuance evidence after its monitoring plan is collected.""" + with _PLAN_SEALS_LOCK: + _PLAN_SEALS.pop(plan_id, None) + + +def _register_plan_seal(plan: object, seal: str) -> None: + """Bind one live monitoring-plan identity to evidence outside writable slots.""" + plan_id = id(plan) + with _PLAN_SEALS_LOCK: + _PLAN_SEALS[plan_id] = seal + finalize(plan, _discard_plan_seal, plan_id) + + +def _authoritative_plan_seal(plan: object) -> str | None: + """Return process-local issuance evidence without trusting plan-owned state.""" + with _PLAN_SEALS_LOCK: + return _PLAN_SEALS.get(id(plan)) + + +def _seal_plan(payload_json: str) -> str: + """Bind one process-local issuance to its exact canonical monitoring-plan bytes.""" + return hmac.new(_PROCESS_PLAN_SEAL_KEY, payload_json.encode("utf-8"), "sha256").hexdigest() def _validate_operational_uuid(value: str, field_name: str) -> None: @@ -104,7 +136,7 @@ def _canonical_timestamp(value: datetime) -> str: return value.isoformat().replace("+00:00", "Z") -@dataclass(frozen=True, slots=True, repr=False) +@dataclass(frozen=True, slots=True, repr=False, weakref_slot=True) class SelectionOutcomeMonitoringPlan: """Immutable aggregate-monitoring plan awaiting accountable human review.""" @@ -215,50 +247,62 @@ def __post_init__(self) -> None: raise ValueError("review_state must remain requires_human_review") if type(self.next_action) is not str or self.next_action != _NEXT_ACTION: raise ValueError("next_action must remain the governed monitoring instruction") + _register_plan_seal(self, _seal_plan(_canonical_plan_json_unchecked(self))) def __repr__(self) -> str: """Return a fully redacted representation safe for routine logs and assertions.""" return "SelectionOutcomeMonitoringPlan()" def canonical_json(self) -> str: - """Return deterministic canonical JSON for immutable audit correlation.""" - payload = { - "actor_reference": self.actor_reference, - "analysis_scope": self.analysis_scope, - "contains_individual_records": self.contains_individual_records, - "decision_authority": self.decision_authority, - "evidence_version": self.evidence_version, - "generated_at": _canonical_timestamp(self.generated_at), - "human_confirmation_required": self.human_confirmation_required, - "job_profile_reference": self.job_profile_reference, - "monitoring_end": self.monitoring_end.isoformat(), - "monitoring_plan_reference": self.monitoring_plan_reference, - "monitoring_start": self.monitoring_start.isoformat(), - "next_action": self.next_action, - "outcome_snapshot_digest": self.outcome_snapshot_digest, - "outcome_snapshot_reference": self.outcome_snapshot_reference, - "population_snapshot_digest": self.population_snapshot_digest, - "population_snapshot_reference": self.population_snapshot_reference, - "protected_attribute_policy_digest": self.protected_attribute_policy_digest, - "protected_attribute_policy_reference": self.protected_attribute_policy_reference, - "purpose_code": self.purpose_code, - "reason_code": self.reason_code, - "review_state": self.review_state, - "reviewer_reference": self.reviewer_reference, - "selection_process_reference": self.selection_process_reference, - "small_sample_policy_digest": self.small_sample_policy_digest, - "small_sample_policy_reference": self.small_sample_policy_reference, - "statistical_plan_digest": self.statistical_plan_digest, - "statistical_plan_reference": self.statistical_plan_reference, - "tenant_record_id": self.tenant_record_id, - } - return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + """Return issuance-verified deterministic JSON for immutable audit correlation.""" + payload_json = _canonical_plan_json_unchecked(self) + authoritative_seal = _authoritative_plan_seal(self) + if authoritative_seal is None: + raise ValueError("selection monitoring plan issuance evidence is unavailable") + if not hmac.compare_digest(authoritative_seal, _seal_plan(payload_json)): + raise ValueError("selection monitoring plan evidence changed after issuance") + return payload_json def sha256_digest(self) -> str: - """Return SHA-256 over the exact canonical UTF-8 monitoring plan.""" + """Return SHA-256 over the exact issuance-verified UTF-8 monitoring plan.""" return sha256(self.canonical_json().encode("utf-8")).hexdigest() +def _canonical_plan_json_unchecked(plan: SelectionOutcomeMonitoringPlan) -> str: + """Render canonical bytes without consulting process-local issuance state.""" + payload = { + "actor_reference": plan.actor_reference, + "analysis_scope": plan.analysis_scope, + "contains_individual_records": plan.contains_individual_records, + "decision_authority": plan.decision_authority, + "evidence_version": plan.evidence_version, + "generated_at": _canonical_timestamp(plan.generated_at), + "human_confirmation_required": plan.human_confirmation_required, + "job_profile_reference": plan.job_profile_reference, + "monitoring_end": plan.monitoring_end.isoformat(), + "monitoring_plan_reference": plan.monitoring_plan_reference, + "monitoring_start": plan.monitoring_start.isoformat(), + "next_action": plan.next_action, + "outcome_snapshot_digest": plan.outcome_snapshot_digest, + "outcome_snapshot_reference": plan.outcome_snapshot_reference, + "population_snapshot_digest": plan.population_snapshot_digest, + "population_snapshot_reference": plan.population_snapshot_reference, + "protected_attribute_policy_digest": plan.protected_attribute_policy_digest, + "protected_attribute_policy_reference": plan.protected_attribute_policy_reference, + "purpose_code": plan.purpose_code, + "reason_code": plan.reason_code, + "review_state": plan.review_state, + "reviewer_reference": plan.reviewer_reference, + "selection_process_reference": plan.selection_process_reference, + "small_sample_policy_digest": plan.small_sample_policy_digest, + "small_sample_policy_reference": plan.small_sample_policy_reference, + "statistical_plan_digest": plan.statistical_plan_digest, + "statistical_plan_reference": plan.statistical_plan_reference, + "tenant_record_id": plan.tenant_record_id, + } + return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + + def build_selection_outcome_monitoring_plan( *, tenant_record_id: str, From c5143703b4c554ba753a86ea54d1ce83f27ef799 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:10:21 -0700 Subject: [PATCH 73/95] docs(selection-monitoring): record issuance integrity repair --- packages/selection-monitoring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 7eaff7ac5..8775c647d 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -9,3 +9,4 @@ All notable package changes are recorded here. - Require every packet reference to be re-resolved within the exact tenant through its authoritative boundary before actor separation, Job-scope verification, or accountable review, preventing cross-tenant evidence mixing behind valid opaque UUIDs. - Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. - Freeze `generated_at` to a detached built-in UTC instant at issuance, reject future generation times, normalize caller timezone-provider failures to fail-closed validation errors, and prevent later mutable `tzinfo` behavior from rewriting canonical monitoring evidence. +- Bind each live issued monitoring plan to its exact construction-time canonical bytes with a process-local HMAC seal stored outside packet-writable slots; canonical export now fails closed if valid evidence fields are rewritten after issuance or the process-local issuance record is unavailable. This is defense-in-depth only: durable uniqueness, authorization, and immutable audit/outbox remain authoritative host/persistence responsibilities. From dc6516d01f82a83d589a34eeafc8d0672d281360 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:10:43 -0700 Subject: [PATCH 74/95] docs(selection-monitoring): explain process-local issuance seal --- packages/selection-monitoring/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index c49f8b0aa..90aabefaa 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -10,6 +10,8 @@ A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the `generated_at` is issuance-time evidence rather than a caller-controlled timezone object retained for later execution. Construction requires an exact built-in `datetime`, resolves any concrete `tzinfo` offset once, converts the result to a built-in UTC `datetime`, rejects future instants, and stores only that detached UTC instant. Later canonical export never invokes the caller's original timezone provider, so a mutable or stateful `tzinfo` cannot rewrite already-issued evidence. Provider exceptions, missing concrete offsets, and UTC-normalization overflow fail closed as `ValueError`; low-level reinjection of a non-UTC timestamp also fails before evidence emission. +A frozen dataclass is not by itself issuance evidence because low-level Python mutation can still rewrite otherwise valid values. Each live issued plan is therefore bound to its exact construction-time canonical JSON by a process-local HMAC seal stored outside packet-writable slots. `canonical_json()` snapshots the current canonical bytes once, verifies that exact snapshot against the external issuance seal, and returns the verified snapshot rather than rereading the object. A valid-value rewrite after issuance or missing process-local issuance evidence fails closed. This mechanism is defense-in-depth for in-process misuse only; durable cross-process uniqueness, purpose authorization, and immutable audit/outbox remain responsibilities of the authoritative host or persistence boundary. + The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose tenant, Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. ## Governance boundary From 1f389c998751fc61225dbddfcd8521f4db2b1657 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:11:04 -0700 Subject: [PATCH 75/95] docs(selection-monitoring): trace issuance tamper evidence --- docs/traceability/selection-outcome-monitoring.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index ad81d2fdf..37de79cd3 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -18,13 +18,14 @@ | Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | | Preserve replayable audit correlation without caller-owned timezone behavior | `generated_at` is resolved once to a built-in UTC instant at issuance; future instants, missing/raising offsets and normalization overflow fail closed; canonical JSON and SHA-256 reuse only the detached UTC value | The packet timestamp proves evidence chronology/correlation, not source truth or scientific/legal validity | +| Prevent valid-value evidence rewrites after issuance | A process-local HMAC seal is stored outside packet-writable slots over the exact construction-time canonical JSON; export snapshots once, verifies that exact snapshot, and fails closed if evidence changed or issuance state is unavailable | The process-local seal is defense-in-depth only and is not durable cross-process authorization, persistence uniqueness, or immutable audit/outbox evidence | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth and that missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. ## Ownership boundary -This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Tenant UUID generation/privacy policy and authoritative tenant-scoped reference/actor resolution remain at the host/core boundary; this packet fails closed by requiring that proof before human review use. +This slice writes only Orgmetra and introduces no database migration or cross-service SQL. Future statistical computation must use the appropriate published psychometric/statistical service contract rather than duplicating foreign kernels, and future access to protected-attribute data must remain purpose-bound and minimum-necessary. Tenant UUID generation/privacy policy and authoritative tenant-scoped reference/actor resolution remain at the host/core boundary; this packet fails closed by requiring that proof before human review use. The process-local issuance registry is deliberately not a distributed attestation store: durable uniqueness, authorization, retention, and immutable audit/outbox remain responsibilities of authoritative Orgmetra persistence/host boundaries. From 80dff1dd68374c33a9a6eec34ee592b87c4549bb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:13:13 -0700 Subject: [PATCH 76/95] test(selection-monitoring): reject digest string subclasses --- .../tests/test_string_runtime_evidence_integrity.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py b/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py index f543812b8..07fdda7ab 100644 --- a/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py +++ b/packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py @@ -55,6 +55,10 @@ def __hash__(self) -> int: return hash("quarterly_selection_governance") +class DigestSubclass(str): + """Semantically valid-looking digest carried by an untrusted runtime subclass.""" + + def valid_kwargs() -> dict[str, object]: """Return one otherwise valid monitoring-plan input.""" return { @@ -116,3 +120,12 @@ def test_rejects_reason_code_string_subclass_that_can_forge_closed_code_membersh with pytest.raises(ValueError, match="reason_code"): build_selection_outcome_monitoring_plan(**kwargs) + + +def test_rejects_digest_string_subclass_before_canonical_evidence_binding() -> None: + """Digest evidence must use the same exact built-in string boundary as other trust text.""" + kwargs = valid_kwargs() + kwargs["population_snapshot_digest"] = DigestSubclass("a" * 64) + + with pytest.raises(ValueError, match="population_snapshot_digest"): + build_selection_outcome_monitoring_plan(**kwargs) From 7496d93592b35e1637b99a90b1147b929a651f78 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:14:13 -0700 Subject: [PATCH 77/95] fix(selection-monitoring): require exact digest strings --- .../src/orgmetra_selection_monitoring/plan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index c28f863d3..2d949f28e 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -105,8 +105,8 @@ def _validate_reference(value: str, prefix: str, field_name: str) -> None: def _validate_digest(value: str, field_name: str) -> None: - """Require lowercase SHA-256 hexadecimal evidence.""" - if not isinstance(value, str) or not _DIGEST_PATTERN.fullmatch(value): + """Require exact built-in lowercase SHA-256 hexadecimal evidence.""" + if type(value) is not str or not _DIGEST_PATTERN.fullmatch(value): raise ValueError(f"{field_name} must be lowercase SHA-256 hex") From b92cfa9bf2cea36f79407aca60e7a81eb44d65f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:14:46 -0700 Subject: [PATCH 78/95] docs(selection-monitoring): inventory runtime integrity regressions --- docs/traceability/selection-outcome-monitoring.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index 37de79cd3..f7d5d4c6f 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -11,7 +11,7 @@ | Monitor the correct hiring/promotion process | Canonical non-sentinel `tenant_record_id` under the Orgmetra core operational-UUID contract, exact UUIDv4-backed `job_profile_reference` and `selection_process_reference`; fixed `analysis_scope=total_selection_process_by_job`; immutable next action requires every packet reference to be re-resolved within exact `tenant_record_id` | UUID syntax alone is not tenant authority or component-level causality evidence | | Reproduce the monitored population and outcomes | Exact UUIDv4-backed aggregate population/outcome snapshot references plus independent SHA-256 digests | No candidate-level record or protected-attribute value in the packet | | Preserve privacy and interpretation rules | Exact UUIDv4-backed protected-attribute handling and small-sample policy references/digests | No blanket authorization to expose protected-attribute data | -| Prevent semantic/value/correlation smuggling through packet-owned references without duplicating tenant identity policy | `tenant_record_id` is canonical/non-sentinel under the authoritative core contract; every packet-owned governed reference requires canonical non-sentinel UUIDv4 plus its expected prefix; `test_reference_privacy.py` covers authoritative UUIDv7 tenant interoperability plus value-bearing, sentinel, noncanonical and UUIDv1 reference cases through builder and `dataclasses.replace(...)` paths | UUID syntax does not prove source truth, tenant membership, or authorization | +| Prevent semantic/value/correlation smuggling through packet-owned references without duplicating tenant identity policy | `tenant_record_id` is canonical/non-sentinel under the authoritative core contract; every packet-owned governed reference requires canonical non-sentinel UUIDv4 plus its expected prefix; digest and governance text evidence require exact built-in strings before validation | UUID syntax does not prove source truth, tenant membership, or authorization | | Prevent cross-tenant evidence mixing | `test_actor_separation.py` requires the governed next action to re-resolve every packet reference within `tenant_record_id` before actor separation, Job scope verification, or accountable review | The packet does not itself query authoritative stores | | Bind the analysis method before interpretation | Exact UUIDv4-backed statistical-plan reference/digest | No statistics are calculated by this package | | Version actor/purpose/reason evidence explicitly | `evidence_version` is a true positive integer through signed-int32 max and participates in canonical JSON/SHA-256 | `test_evidence_version.py` proves presence, digest separation, bounds, and `dataclasses.replace(...)` revalidation | @@ -22,7 +22,7 @@ ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth and that missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth and that missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py` proves fixed governance fields reject hostile runtime string subclasses before equality-based policy checks. `packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py` proves tenant, reference, purpose, reason, and SHA-256 digest evidence reject caller-defined string subclasses before canonical evidence binding. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. From 6edd5281c8feb95dcdfae7f9485207c933e48d0e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 13:16:00 -0700 Subject: [PATCH 79/95] docs(selection-monitoring): record exact digest runtime contract --- packages/selection-monitoring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 8775c647d..7c1f97559 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -10,3 +10,4 @@ All notable package changes are recorded here. - Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. - Freeze `generated_at` to a detached built-in UTC instant at issuance, reject future generation times, normalize caller timezone-provider failures to fail-closed validation errors, and prevent later mutable `tzinfo` behavior from rewriting canonical monitoring evidence. - Bind each live issued monitoring plan to its exact construction-time canonical bytes with a process-local HMAC seal stored outside packet-writable slots; canonical export now fails closed if valid evidence fields are rewritten after issuance or the process-local issuance record is unavailable. This is defense-in-depth only: durable uniqueness, authorization, and immutable audit/outbox remain authoritative host/persistence responsibilities. +- Require SHA-256 digest evidence to be exact built-in strings before regex validation and canonical binding, matching the package's strict runtime-type policy for other trust-bearing text and rejecting caller-defined `str` subclasses. From fca40417cfc60947a5836cf1a90815fdf118b889 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 13:58:11 +0900 Subject: [PATCH 80/95] test(selection-monitoring): cover copied issuance plans --- .../tests/test_issuance_integrity.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/selection-monitoring/tests/test_issuance_integrity.py b/packages/selection-monitoring/tests/test_issuance_integrity.py index 7dc71f06d..472daa021 100644 --- a/packages/selection-monitoring/tests/test_issuance_integrity.py +++ b/packages/selection-monitoring/tests/test_issuance_integrity.py @@ -2,7 +2,9 @@ from __future__ import annotations +import copy from datetime import date, datetime, timezone +import pickle import pytest @@ -61,3 +63,17 @@ def test_missing_process_local_issuance_evidence_fails_closed() -> None: with pytest.raises(ValueError, match="issuance evidence is unavailable"): plan.canonical_json() + + +def test_copied_or_serialized_plan_fails_closed_without_issuance_evidence() -> None: + """Reject copies and serialized plans that bypass the original issuance seal.""" + plan = _build_plan() + clones = ( + copy.copy(plan), + copy.deepcopy(plan), + pickle.loads(pickle.dumps(plan)), + ) + + for clone in clones: + with pytest.raises(ValueError, match="issuance evidence is unavailable"): + clone.canonical_json() From 3be942d562b7220bdf92c0888f8d32b023937d94 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:28:16 -0700 Subject: [PATCH 81/95] test(selection-monitoring): reject live reference reissuance --- .../tests/test_issuance_integrity.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/selection-monitoring/tests/test_issuance_integrity.py b/packages/selection-monitoring/tests/test_issuance_integrity.py index 472daa021..a15460458 100644 --- a/packages/selection-monitoring/tests/test_issuance_integrity.py +++ b/packages/selection-monitoring/tests/test_issuance_integrity.py @@ -4,7 +4,9 @@ import copy from datetime import date, datetime, timezone +import gc import pickle +import weakref import pytest @@ -77,3 +79,31 @@ def test_copied_or_serialized_plan_fails_closed_without_issuance_evidence() -> N for clone in clones: with pytest.raises(ValueError, match="issuance evidence is unavailable"): clone.canonical_json() + + +def test_live_monitoring_reference_cannot_be_reissued() -> None: + """Reject a second live issuance using the same tenant-qualified plan reference.""" + first = _build_plan() + + with pytest.raises(ValueError, match="monitoring_plan_reference already has a live issuance"): + _build_plan() + + conflicting_kwargs = dict(_BASE_KWARGS) + conflicting_kwargs["population_snapshot_digest"] = "f" * 64 + with pytest.raises(ValueError, match="monitoring_plan_reference already has a live issuance"): + build_selection_outcome_monitoring_plan(**conflicting_kwargs) + + assert first.canonical_json() + + +def test_live_monitoring_reference_binding_is_released_after_collection() -> None: + """Release the process-local uniqueness guard when the issued plan is collected.""" + plan = _build_plan() + observed = weakref.ref(plan) + + del plan + gc.collect() + + assert observed() is None + rebuilt = _build_plan() + assert rebuilt.canonical_json() From acc99ff06f9ef67084bf0945557c84a7dc25ed5d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:29:55 -0700 Subject: [PATCH 82/95] fix(selection-monitoring): bind live plan references --- .../src/orgmetra_selection_monitoring/plan.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 2d949f28e..8dae0ee25 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -39,6 +39,7 @@ ) _PROCESS_PLAN_SEAL_KEY = secrets.token_bytes(32) _PLAN_SEALS: dict[int, str] = {} +_PLAN_REFERENCE_BINDINGS: set[tuple[str, str]] = set() _PLAN_SEALS_LOCK = RLock() @@ -48,6 +49,22 @@ def _discard_plan_seal(plan_id: int) -> None: _PLAN_SEALS.pop(plan_id, None) +def _discard_plan_reference_binding(binding: tuple[str, str]) -> None: + """Release one process-local tenant-qualified monitoring-plan reference binding.""" + with _PLAN_SEALS_LOCK: + _PLAN_REFERENCE_BINDINGS.discard(binding) + + +def _register_plan_reference_binding(plan: SelectionOutcomeMonitoringPlan) -> None: + """Reject ambiguous simultaneous issuance under one tenant-qualified plan reference.""" + binding = (plan.tenant_record_id, plan.monitoring_plan_reference) + with _PLAN_SEALS_LOCK: + if binding in _PLAN_REFERENCE_BINDINGS: + raise ValueError("monitoring_plan_reference already has a live issuance") + _PLAN_REFERENCE_BINDINGS.add(binding) + finalize(plan, _discard_plan_reference_binding, binding) + + def _register_plan_seal(plan: object, seal: str) -> None: """Bind one live monitoring-plan identity to evidence outside writable slots.""" plan_id = id(plan) @@ -247,6 +264,7 @@ def __post_init__(self) -> None: raise ValueError("review_state must remain requires_human_review") if type(self.next_action) is not str or self.next_action != _NEXT_ACTION: raise ValueError("next_action must remain the governed monitoring instruction") + _register_plan_reference_binding(self) _register_plan_seal(self, _seal_plan(_canonical_plan_json_unchecked(self))) def __repr__(self) -> str: From 5bb5fb3c6ce5af88bb4eebf0e8b0b5697dd08442 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:30:40 -0700 Subject: [PATCH 83/95] revert(selection-monitoring): preserve versioned reference semantics --- .../tests/test_issuance_integrity.py | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/packages/selection-monitoring/tests/test_issuance_integrity.py b/packages/selection-monitoring/tests/test_issuance_integrity.py index a15460458..472daa021 100644 --- a/packages/selection-monitoring/tests/test_issuance_integrity.py +++ b/packages/selection-monitoring/tests/test_issuance_integrity.py @@ -4,9 +4,7 @@ import copy from datetime import date, datetime, timezone -import gc import pickle -import weakref import pytest @@ -79,31 +77,3 @@ def test_copied_or_serialized_plan_fails_closed_without_issuance_evidence() -> N for clone in clones: with pytest.raises(ValueError, match="issuance evidence is unavailable"): clone.canonical_json() - - -def test_live_monitoring_reference_cannot_be_reissued() -> None: - """Reject a second live issuance using the same tenant-qualified plan reference.""" - first = _build_plan() - - with pytest.raises(ValueError, match="monitoring_plan_reference already has a live issuance"): - _build_plan() - - conflicting_kwargs = dict(_BASE_KWARGS) - conflicting_kwargs["population_snapshot_digest"] = "f" * 64 - with pytest.raises(ValueError, match="monitoring_plan_reference already has a live issuance"): - build_selection_outcome_monitoring_plan(**conflicting_kwargs) - - assert first.canonical_json() - - -def test_live_monitoring_reference_binding_is_released_after_collection() -> None: - """Release the process-local uniqueness guard when the issued plan is collected.""" - plan = _build_plan() - observed = weakref.ref(plan) - - del plan - gc.collect() - - assert observed() is None - rebuilt = _build_plan() - assert rebuilt.canonical_json() From a9823aaff3364971cca0d42134864c21fde27c49 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 21:31:14 -0700 Subject: [PATCH 84/95] revert(selection-monitoring): retain revision evidence semantics --- .../src/orgmetra_selection_monitoring/plan.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 8dae0ee25..2d949f28e 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -39,7 +39,6 @@ ) _PROCESS_PLAN_SEAL_KEY = secrets.token_bytes(32) _PLAN_SEALS: dict[int, str] = {} -_PLAN_REFERENCE_BINDINGS: set[tuple[str, str]] = set() _PLAN_SEALS_LOCK = RLock() @@ -49,22 +48,6 @@ def _discard_plan_seal(plan_id: int) -> None: _PLAN_SEALS.pop(plan_id, None) -def _discard_plan_reference_binding(binding: tuple[str, str]) -> None: - """Release one process-local tenant-qualified monitoring-plan reference binding.""" - with _PLAN_SEALS_LOCK: - _PLAN_REFERENCE_BINDINGS.discard(binding) - - -def _register_plan_reference_binding(plan: SelectionOutcomeMonitoringPlan) -> None: - """Reject ambiguous simultaneous issuance under one tenant-qualified plan reference.""" - binding = (plan.tenant_record_id, plan.monitoring_plan_reference) - with _PLAN_SEALS_LOCK: - if binding in _PLAN_REFERENCE_BINDINGS: - raise ValueError("monitoring_plan_reference already has a live issuance") - _PLAN_REFERENCE_BINDINGS.add(binding) - finalize(plan, _discard_plan_reference_binding, binding) - - def _register_plan_seal(plan: object, seal: str) -> None: """Bind one live monitoring-plan identity to evidence outside writable slots.""" plan_id = id(plan) @@ -264,7 +247,6 @@ def __post_init__(self) -> None: raise ValueError("review_state must remain requires_human_review") if type(self.next_action) is not str or self.next_action != _NEXT_ACTION: raise ValueError("next_action must remain the governed monitoring instruction") - _register_plan_reference_binding(self) _register_plan_seal(self, _seal_plan(_canonical_plan_json_unchecked(self))) def __repr__(self) -> str: From 7bfc85bd0b1491e97c9b2e9765c3669acea27966 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:29:50 -0700 Subject: [PATCH 85/95] test(selection-monitoring): reject issuance seal renewal --- .../tests/test_issuance_integrity.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/selection-monitoring/tests/test_issuance_integrity.py b/packages/selection-monitoring/tests/test_issuance_integrity.py index 472daa021..1eb929c79 100644 --- a/packages/selection-monitoring/tests/test_issuance_integrity.py +++ b/packages/selection-monitoring/tests/test_issuance_integrity.py @@ -77,3 +77,17 @@ def test_copied_or_serialized_plan_fails_closed_without_issuance_evidence() -> N for clone in clones: with pytest.raises(ValueError, match="issuance evidence is unavailable"): clone.canonical_json() + + +def test_reinitialization_cannot_renew_issuance_evidence_after_valid_value_rewrite() -> None: + """Keep one live plan identity bound to its original construction evidence.""" + plan = _build_plan() + original = plan.canonical_json() + + object.__setattr__(plan, "population_snapshot_digest", "f" * 64) + + with pytest.raises(ValueError, match="issuance evidence already exists"): + plan.__post_init__() + with pytest.raises(ValueError, match="changed after issuance"): + plan.canonical_json() + assert original != plan_module._canonical_plan_json_unchecked(plan) From 051608dc642a46ebb91edca696622fc69bbaa0aa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:32:00 -0700 Subject: [PATCH 86/95] fix(selection-monitoring): prevent issuance seal renewal --- .../src/orgmetra_selection_monitoring/plan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 2d949f28e..470be8d6c 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -49,9 +49,11 @@ def _discard_plan_seal(plan_id: int) -> None: def _register_plan_seal(plan: object, seal: str) -> None: - """Bind one live monitoring-plan identity to evidence outside writable slots.""" + """Bind one live monitoring-plan identity exactly once outside writable slots.""" plan_id = id(plan) with _PLAN_SEALS_LOCK: + if plan_id in _PLAN_SEALS: + raise ValueError("selection monitoring plan issuance evidence already exists") _PLAN_SEALS[plan_id] = seal finalize(plan, _discard_plan_seal, plan_id) From f5d91b15ed20b243fc9a50ae3b529eac1ace4046 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:34:02 -0700 Subject: [PATCH 87/95] docs(selection-monitoring): bind single-use issuance seal --- docs/adr/0016-governed-selection-outcome-monitoring-plan.md | 3 +++ docs/traceability/selection-outcome-monitoring.md | 4 ++-- packages/selection-monitoring/CHANGELOG.md | 2 +- packages/selection-monitoring/README.md | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md index 57d5c0f7a..cae247beb 100644 --- a/docs/adr/0016-governed-selection-outcome-monitoring-plan.md +++ b/docs/adr/0016-governed-selection-outcome-monitoring-plan.md @@ -27,6 +27,8 @@ UUID syntax is not tenant authority. Before review, the host must re-resolve **e The contract is aggregate-only and carries no candidate identity, protected-attribute value, individual assessment score, individual employment decision, or free-form model output. It fixes `analysis_scope` to `total_selection_process_by_job`, `decision_authority` to `human_review_only`, and state to `requires_human_review`. It does not calculate selection rates, mechanically apply the four-fifths heuristic, test statistical significance, infer discrimination, or authorize a process change. +Each live plan identity receives one process-local construction seal over its exact canonical bytes. Seal registration is one-shot: a repeated `__post_init__()` call cannot overwrite the original seal, including after low-level mutation to another syntactically valid value. Canonical export therefore continues to compare the live payload with the original construction evidence instead of permitting reinitialization to renew trust. This runtime mechanism is defense-in-depth only and does not replace durable immutable audit/outbox evidence, persistence uniqueness, or cross-process authorization. + Any later analytics or persistence boundary must independently enforce purpose-bound authorization, authoritative tenant-scoped reference and actor resolution, minimum-necessary protected-attribute access, small-sample controls, provenance, immutable audit evidence, and accountable human interpretation. Results are evidence for review, not an automated high-impact employment decision or certification/legal conclusion. ## Consequences @@ -36,6 +38,7 @@ Any later analytics or persistence boundary must independently enforce purpose-b - Privacy risk is reduced because individual protected-attribute values and candidate records remain outside the plan envelope and packet-owned trust references reject UUIDv1 timestamp/node metadata and value-bearing suffixes without making the leaf package incompatible with authoritative Orgmetra tenant UUIDs. - Cross-tenant evidence mixing is fail-closed at the host review boundary because every opaque reference must be re-resolved in the exact packet tenant. - Requester/reviewer separation is proven from authoritative resolved actor identities rather than inferred from different opaque strings. +- A valid-value low-level rewrite cannot be legitimized by re-running dataclass initialization because process-local seal registration is single-use for the live identity. - The four-fifths rule cannot be represented as an automatic pass/fail legal rule by this contract; interpretation remains with authorized analysts and accountable humans. - Psychometric/statistical production compute remains owned by the appropriate Psychometrics Commons / fast-mlsirm / TEPP contract when those kernels are needed. diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index f7d5d4c6f..e6993cbd1 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -18,11 +18,11 @@ | Prove accountable requester/reviewer separation | Different opaque actor references as a syntactic guard plus tenant-scoped authoritative resolution requiring distinct resolved actor identities | Reference inequality alone is not identity or separation-of-duties evidence | | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | | Preserve replayable audit correlation without caller-owned timezone behavior | `generated_at` is resolved once to a built-in UTC instant at issuance; future instants, missing/raising offsets and normalization overflow fail closed; canonical JSON and SHA-256 reuse only the detached UTC value | The packet timestamp proves evidence chronology/correlation, not source truth or scientific/legal validity | -| Prevent valid-value evidence rewrites after issuance | A process-local HMAC seal is stored outside packet-writable slots over the exact construction-time canonical JSON; export snapshots once, verifies that exact snapshot, and fails closed if evidence changed or issuance state is unavailable | The process-local seal is defense-in-depth only and is not durable cross-process authorization, persistence uniqueness, or immutable audit/outbox evidence | +| Prevent valid-value evidence rewrites after issuance | A process-local HMAC seal is stored outside packet-writable slots over the exact construction-time canonical JSON; seal registration is single-use per live identity, so repeated `__post_init__()` cannot renew trust; export verifies the current snapshot against the original seal and fails closed if evidence changed or issuance state is unavailable | The process-local seal is defense-in-depth only and is not durable cross-process authorization, persistence uniqueness, or immutable audit/outbox evidence | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth and that missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py` proves fixed governance fields reject hostile runtime string subclasses before equality-based policy checks. `packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py` proves tenant, reference, purpose, reason, and SHA-256 digest evidence reject caller-defined string subclasses before canonical evidence binding. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth, repeated `__post_init__()` cannot overwrite the original construction seal, and missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py` proves fixed governance fields reject hostile runtime string subclasses before equality-based policy checks. `packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py` proves tenant, reference, purpose, reason, and SHA-256 digest evidence reject caller-defined string subclasses before canonical evidence binding. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index 7c1f97559..f521d4639 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -9,5 +9,5 @@ All notable package changes are recorded here. - Require every packet reference to be re-resolved within the exact tenant through its authoritative boundary before actor separation, Job-scope verification, or accountable review, preventing cross-tenant evidence mixing behind valid opaque UUIDs. - Bind a true positive `evidence_version` (1..2147483647) into canonical JSON and SHA-256 evidence so revisions to high-impact monitoring evidence cannot silently collide. - Freeze `generated_at` to a detached built-in UTC instant at issuance, reject future generation times, normalize caller timezone-provider failures to fail-closed validation errors, and prevent later mutable `tzinfo` behavior from rewriting canonical monitoring evidence. -- Bind each live issued monitoring plan to its exact construction-time canonical bytes with a process-local HMAC seal stored outside packet-writable slots; canonical export now fails closed if valid evidence fields are rewritten after issuance or the process-local issuance record is unavailable. This is defense-in-depth only: durable uniqueness, authorization, and immutable audit/outbox remain authoritative host/persistence responsibilities. +- Bind each live issued monitoring plan to its exact construction-time canonical bytes with a process-local HMAC seal stored outside packet-writable slots; seal registration is single-use per live identity, so a low-level valid-value rewrite followed by repeated `__post_init__()` cannot renew trust. Canonical export fails closed if evidence is rewritten, seal renewal is attempted, or process-local issuance evidence is unavailable. This is defense-in-depth only: durable uniqueness, authorization, and immutable audit/outbox remain authoritative host/persistence responsibilities. - Require SHA-256 digest evidence to be exact built-in strings before regex validation and canonical binding, matching the package's strict runtime-type policy for other trust-bearing text and rejecting caller-defined `str` subclasses. diff --git a/packages/selection-monitoring/README.md b/packages/selection-monitoring/README.md index 90aabefaa..cf735e4fb 100644 --- a/packages/selection-monitoring/README.md +++ b/packages/selection-monitoring/README.md @@ -10,7 +10,7 @@ A `SelectionOutcomeMonitoringPlan` ties one tenant and authoritative Job to the `generated_at` is issuance-time evidence rather than a caller-controlled timezone object retained for later execution. Construction requires an exact built-in `datetime`, resolves any concrete `tzinfo` offset once, converts the result to a built-in UTC `datetime`, rejects future instants, and stores only that detached UTC instant. Later canonical export never invokes the caller's original timezone provider, so a mutable or stateful `tzinfo` cannot rewrite already-issued evidence. Provider exceptions, missing concrete offsets, and UTC-normalization overflow fail closed as `ValueError`; low-level reinjection of a non-UTC timestamp also fails before evidence emission. -A frozen dataclass is not by itself issuance evidence because low-level Python mutation can still rewrite otherwise valid values. Each live issued plan is therefore bound to its exact construction-time canonical JSON by a process-local HMAC seal stored outside packet-writable slots. `canonical_json()` snapshots the current canonical bytes once, verifies that exact snapshot against the external issuance seal, and returns the verified snapshot rather than rereading the object. A valid-value rewrite after issuance or missing process-local issuance evidence fails closed. This mechanism is defense-in-depth for in-process misuse only; durable cross-process uniqueness, purpose authorization, and immutable audit/outbox remain responsibilities of the authoritative host or persistence boundary. +A frozen dataclass is not by itself issuance evidence because low-level Python mutation can still rewrite otherwise valid values. Each live issued plan is therefore bound to its exact construction-time canonical JSON by a process-local HMAC seal stored outside packet-writable slots. Seal registration is single-use per live object identity: explicitly re-running `__post_init__()` cannot replace an existing seal after a low-level valid-value rewrite. `canonical_json()` snapshots the current canonical bytes once, verifies that exact snapshot against the external issuance seal, and returns the verified snapshot rather than rereading the object. A valid-value rewrite, attempted seal renewal, or missing process-local issuance evidence fails closed. This mechanism is defense-in-depth for in-process misuse only; durable cross-process uniqueness, purpose authorization, and immutable audit/outbox remain responsibilities of the authoritative host or persistence boundary. The ordinary representation is fully redacted as `SelectionOutcomeMonitoringPlan()`, so routine logs and assertion failures do not expose tenant, Job, actor, policy, snapshot, or statistical-plan correlations. Canonical JSON remains the explicit evidence serialization boundary. UUID-backed correlations are value-minimized metadata, not anonymous data, and remain subject to purpose-bound authorization, least privilege, retention/export controls, and audit. From 6760e4d1d358e0ae3dd90d2021254604314112f8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 15:27:29 -0700 Subject: [PATCH 88/95] test(selection-monitoring): require shared config quality triggers --- .../tests/test_quality_workflow_trigger.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/selection-monitoring/tests/test_quality_workflow_trigger.py diff --git a/packages/selection-monitoring/tests/test_quality_workflow_trigger.py b/packages/selection-monitoring/tests/test_quality_workflow_trigger.py new file mode 100644 index 000000000..d54de549b --- /dev/null +++ b/packages/selection-monitoring/tests/test_quality_workflow_trigger.py @@ -0,0 +1,27 @@ +"""Regression tests for the selection-monitoring quality-gate trigger surface.""" + +from pathlib import Path + + +_WORKFLOW_PATH = Path(".github/workflows/selection-monitoring-quality.yml") +_SHARED_TEST_CONFIGURATION = ( + ".gitignore", + ".python-version", + "conftest.py", + "packages/conftest.py", + "pyproject.toml", + "pytest.ini", + "setup.cfg", + "tox.ini", +) + + +def test_quality_workflow_retriggers_on_shared_test_configuration() -> None: + """Require every shared test/runtime configuration input to retrigger this gate.""" + workflow = _WORKFLOW_PATH.read_text(encoding="utf-8") + + for path in _SHARED_TEST_CONFIGURATION: + assert f'- "{path}"' in workflow, ( + f"{path} can change package test or clean-checkout behavior and must retrigger " + "Selection Monitoring Quality" + ) From e4ddfa404ac7fdded79e709a78f54729fb54fc08 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 15:28:18 -0700 Subject: [PATCH 89/95] fix(selection-monitoring): retrigger quality on shared config --- .github/workflows/selection-monitoring-quality.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/selection-monitoring-quality.yml b/.github/workflows/selection-monitoring-quality.yml index 8c7d78160..5c0fcf437 100644 --- a/.github/workflows/selection-monitoring-quality.yml +++ b/.github/workflows/selection-monitoring-quality.yml @@ -8,6 +8,14 @@ on: - "packages/selection-monitoring/**" - ".github/requirements/foundation-test.txt" - ".github/workflows/selection-monitoring-quality.yml" + - ".gitignore" + - ".python-version" + - "conftest.py" + - "packages/conftest.py" + - "pyproject.toml" + - "pytest.ini" + - "setup.cfg" + - "tox.ini" - "docs/adr/0016-governed-selection-outcome-monitoring-plan.md" - "docs/doctoring/selection-outcome-monitoring-references.md" - "docs/traceability/selection-outcome-monitoring.md" From 08d668d8ecf3a19c46798a150f4ef341c0272e18 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 15:30:28 -0700 Subject: [PATCH 90/95] docs(selection-monitoring): record shared-config gate integrity --- packages/selection-monitoring/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/selection-monitoring/CHANGELOG.md b/packages/selection-monitoring/CHANGELOG.md index f521d4639..c69c8a5c1 100644 --- a/packages/selection-monitoring/CHANGELOG.md +++ b/packages/selection-monitoring/CHANGELOG.md @@ -11,3 +11,4 @@ All notable package changes are recorded here. - Freeze `generated_at` to a detached built-in UTC instant at issuance, reject future generation times, normalize caller timezone-provider failures to fail-closed validation errors, and prevent later mutable `tzinfo` behavior from rewriting canonical monitoring evidence. - Bind each live issued monitoring plan to its exact construction-time canonical bytes with a process-local HMAC seal stored outside packet-writable slots; seal registration is single-use per live identity, so a low-level valid-value rewrite followed by repeated `__post_init__()` cannot renew trust. Canonical export fails closed if evidence is rewritten, seal renewal is attempted, or process-local issuance evidence is unavailable. This is defense-in-depth only: durable uniqueness, authorization, and immutable audit/outbox remain authoritative host/persistence responsibilities. - Require SHA-256 digest evidence to be exact built-in strings before regex validation and canonical binding, matching the package's strict runtime-type policy for other trust-bearing text and rejecting caller-defined `str` subclasses. +- Make `Selection Monitoring Quality` retrigger when shared repository Python/test/clean-checkout configuration changes (`.gitignore`, `.python-version`, root/shared `conftest.py`, root `pyproject.toml`, `pytest.ini`, `setup.cfg`, or `tox.ini`) and enforce that trigger surface with an executable regression, preventing stale package-quality evidence after shared tooling changes. From 874e004602c917ce57e269c69ff9217553cb3613 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 15:30:48 -0700 Subject: [PATCH 91/95] docs(selection-monitoring): trace shared-config quality evidence --- docs/traceability/selection-outcome-monitoring.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index e6993cbd1..5073dd5a2 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -19,12 +19,13 @@ | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | | Preserve replayable audit correlation without caller-owned timezone behavior | `generated_at` is resolved once to a built-in UTC instant at issuance; future instants, missing/raising offsets and normalization overflow fail closed; canonical JSON and SHA-256 reuse only the detached UTC value | The packet timestamp proves evidence chronology/correlation, not source truth or scientific/legal validity | | Prevent valid-value evidence rewrites after issuance | A process-local HMAC seal is stored outside packet-writable slots over the exact construction-time canonical JSON; seal registration is single-use per live identity, so repeated `__post_init__()` cannot renew trust; export verifies the current snapshot against the original seal and fails closed if evidence changed or issuance state is unavailable | The process-local seal is defense-in-depth only and is not durable cross-process authorization, persistence uniqueness, or immutable audit/outbox evidence | +| Keep package-quality evidence current when shared repository test/runtime configuration changes | `test_quality_workflow_retriggers_on_shared_test_configuration` requires `.github/workflows/selection-monitoring-quality.yml` to retrigger on shared Python/test/clean-checkout configuration inputs as well as package-owned paths | This package workflow supplements rather than replaces organization-required central review/security workflows | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth, repeated `__post_init__()` cannot overwrite the original construction seal, and missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py` proves fixed governance fields reject hostile runtime string subclasses before equality-based policy checks. `packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py` proves tenant, reference, purpose, reason, and SHA-256 digest evidence reject caller-defined string subclasses before canonical evidence binding. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth, repeated `__post_init__()` cannot overwrite the original construction seal, and missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py` proves fixed governance fields reject hostile runtime string subclasses before equality-based policy checks. `packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py` proves tenant, reference, purpose, reason, and SHA-256 digest evidence reject caller-defined string subclasses before canonical evidence binding. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `packages/selection-monitoring/tests/test_quality_workflow_trigger.py` prevents shared repository Python/test/clean-checkout configuration from changing package verification behavior without retriggering the package quality gate. -`.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, and clean-checkout proof. It does not replace any organization-required central workflow. +`.github/workflows/selection-monitoring-quality.yml` is supplemental exact-head evidence with hash-locked test tooling, 100% owned statement/branch coverage, exact-candidate checkout, clean-checkout proof, and explicit retriggers for package-owned plus shared repository test/runtime configuration. It does not replace any organization-required central workflow. ## Ownership boundary From a2587796734656fdd391377b1ec001e675c3d280 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 00:05:44 -0700 Subject: [PATCH 92/95] test(selection-monitoring): reject seal-loss reissuance --- .../tests/test_issuance_integrity.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/selection-monitoring/tests/test_issuance_integrity.py b/packages/selection-monitoring/tests/test_issuance_integrity.py index 1eb929c79..23a767a64 100644 --- a/packages/selection-monitoring/tests/test_issuance_integrity.py +++ b/packages/selection-monitoring/tests/test_issuance_integrity.py @@ -91,3 +91,18 @@ def test_reinitialization_cannot_renew_issuance_evidence_after_valid_value_rewri with pytest.raises(ValueError, match="changed after issuance"): plan.canonical_json() assert original != plan_module._canonical_plan_json_unchecked(plan) + + +def test_discarded_seal_cannot_be_renewed_after_valid_value_rewrite() -> None: + """Do not let seal loss reset the live plan's single-use issuance lifecycle.""" + plan = _build_plan() + original = plan.canonical_json() + + plan_module._discard_plan_seal(id(plan)) + object.__setattr__(plan, "population_snapshot_digest", "f" * 64) + + with pytest.raises(ValueError, match="issuance evidence already exists"): + plan.__post_init__() + with pytest.raises(ValueError, match="issuance evidence is unavailable"): + plan.canonical_json() + assert original != plan_module._canonical_plan_json_unchecked(plan) From b1d03ed3c6b301b179c0e76e1baafee3f8566375 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 00:07:56 -0700 Subject: [PATCH 93/95] fix(selection-monitoring): preserve single-use issuance after seal loss --- .../src/orgmetra_selection_monitoring/plan.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 470be8d6c..47757522b 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -16,7 +16,7 @@ import secrets from threading import RLock from uuid import UUID -from weakref import finalize +from weakref import WeakValueDictionary, finalize _CODE_PATTERN = re.compile(r"^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$") _DIGEST_PATTERN = re.compile(r"^[0-9a-f]{64}$") @@ -39,11 +39,12 @@ ) _PROCESS_PLAN_SEAL_KEY = secrets.token_bytes(32) _PLAN_SEALS: dict[int, str] = {} +_ISSUED_PLAN_IDENTITIES: WeakValueDictionary[int, object] = WeakValueDictionary() _PLAN_SEALS_LOCK = RLock() def _discard_plan_seal(plan_id: int) -> None: - """Discard process-local issuance evidence after its monitoring plan is collected.""" + """Discard process-local seal bytes without resetting live issuance identity.""" with _PLAN_SEALS_LOCK: _PLAN_SEALS.pop(plan_id, None) @@ -173,6 +174,9 @@ class SelectionOutcomeMonitoringPlan: def __post_init__(self) -> None: """Fail closed when direct construction drifts from the governed contract.""" + with _PLAN_SEALS_LOCK: + if _ISSUED_PLAN_IDENTITIES.get(id(self)) is self: + raise ValueError("selection monitoring plan issuance evidence already exists") _validate_operational_uuid(self.tenant_record_id, "tenant_record_id") _validate_reference( self.monitoring_plan_reference, @@ -250,6 +254,8 @@ def __post_init__(self) -> None: if type(self.next_action) is not str or self.next_action != _NEXT_ACTION: raise ValueError("next_action must remain the governed monitoring instruction") _register_plan_seal(self, _seal_plan(_canonical_plan_json_unchecked(self))) + with _PLAN_SEALS_LOCK: + _ISSUED_PLAN_IDENTITIES[id(self)] = self def __repr__(self) -> str: """Return a fully redacted representation safe for routine logs and assertions.""" From fb03c0837b38424412fa774576a8ded0f9847896 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 00:10:51 -0700 Subject: [PATCH 94/95] fix(selection-monitoring): make issuance registration atomic --- .../src/orgmetra_selection_monitoring/plan.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py index 47757522b..dacd67e95 100644 --- a/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py +++ b/packages/selection-monitoring/src/orgmetra_selection_monitoring/plan.py @@ -50,12 +50,13 @@ def _discard_plan_seal(plan_id: int) -> None: def _register_plan_seal(plan: object, seal: str) -> None: - """Bind one live monitoring-plan identity exactly once outside writable slots.""" + """Atomically bind one live monitoring-plan identity to one issuance seal.""" plan_id = id(plan) with _PLAN_SEALS_LOCK: - if plan_id in _PLAN_SEALS: + if _ISSUED_PLAN_IDENTITIES.get(plan_id) is plan: raise ValueError("selection monitoring plan issuance evidence already exists") _PLAN_SEALS[plan_id] = seal + _ISSUED_PLAN_IDENTITIES[plan_id] = plan finalize(plan, _discard_plan_seal, plan_id) @@ -174,9 +175,6 @@ class SelectionOutcomeMonitoringPlan: def __post_init__(self) -> None: """Fail closed when direct construction drifts from the governed contract.""" - with _PLAN_SEALS_LOCK: - if _ISSUED_PLAN_IDENTITIES.get(id(self)) is self: - raise ValueError("selection monitoring plan issuance evidence already exists") _validate_operational_uuid(self.tenant_record_id, "tenant_record_id") _validate_reference( self.monitoring_plan_reference, @@ -254,8 +252,6 @@ def __post_init__(self) -> None: if type(self.next_action) is not str or self.next_action != _NEXT_ACTION: raise ValueError("next_action must remain the governed monitoring instruction") _register_plan_seal(self, _seal_plan(_canonical_plan_json_unchecked(self))) - with _PLAN_SEALS_LOCK: - _ISSUED_PLAN_IDENTITIES[id(self)] = self def __repr__(self) -> str: """Return a fully redacted representation safe for routine logs and assertions.""" From ec35dc87d6be4f44a2c7b44311fa8c89bb080fb3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 23:34:23 +0900 Subject: [PATCH 95/95] ci(foundation): discover delegated artifact contracts Replace the selection-monitoring-specific dispatcher edit with a stable Foundation-owned discovery convention so later package artifact contracts can be added without competing edits to the shared dependency-hygiene script. Keep the retired leaf workflow deleted, preserve hash-locked 100% coverage, compile before tests, and clean the isolated venv on exit. Signed-off-by: Seongho Bae --- docs/traceability/selection-outcome-monitoring.md | 6 +++--- .../tests/test_quality_workflow_trigger.py | 8 ++++---- tests/test_foundation_ci_dependency_hygiene.sh | 15 +++++++++++---- ...oundation_ci_selection_monitoring_artifact.sh} | 6 ++++++ 4 files changed, 24 insertions(+), 11 deletions(-) rename tests/{test_selection_monitoring_artifact.sh => test_foundation_ci_selection_monitoring_artifact.sh} (86%) diff --git a/docs/traceability/selection-outcome-monitoring.md b/docs/traceability/selection-outcome-monitoring.md index 929777f4b..76a5f25b8 100644 --- a/docs/traceability/selection-outcome-monitoring.md +++ b/docs/traceability/selection-outcome-monitoring.md @@ -19,13 +19,13 @@ | Prevent automated high-impact action | Exact boolean human confirmation, `human_review_only`, `requires_human_review`, governed next action | No automated employment-process change or legal conclusion | | Preserve replayable audit correlation without caller-owned timezone behavior | `generated_at` is resolved once to a built-in UTC instant at issuance; future instants, missing/raising offsets and normalization overflow fail closed; canonical JSON and SHA-256 reuse only the detached UTC value | The packet timestamp proves evidence chronology/correlation, not source truth or scientific/legal validity | | Prevent valid-value evidence rewrites after issuance | A process-local HMAC seal is stored outside packet-writable slots over the exact construction-time canonical JSON; seal registration is single-use per live identity, so repeated `__post_init__()` cannot renew trust; export verifies the current snapshot against the original seal and fails closed if evidence changed or issuance state is unavailable | The process-local seal is defense-in-depth only and is not durable cross-process authorization, persistence uniqueness, or immutable audit/outbox evidence | -| Keep package-quality evidence current under the protected workflow-ownership model | Canonical `Foundation CI` invokes `tests/test_foundation_ci_dependency_hygiene.sh`, which delegates to `tests/test_selection_monitoring_artifact.sh`; the isolated hash-locked package contract runs the package pytest configuration, whose 100% statement and branch coverage thresholds remain authoritative. `test_quality_workflow_trigger.py` fails if the retired leaf workflow reappears or this canonical delegation disappears. | There is no package-local Selection Monitoring workflow and no transfer of historical GREEN across a successor head | +| Keep package-quality evidence current under the protected workflow-ownership model | Canonical `Foundation CI` invokes `tests/test_foundation_ci_dependency_hygiene.sh`, which discovers and executes `tests/test_foundation_ci_*_artifact.sh` contracts; Selection Monitoring is owned by `tests/test_foundation_ci_selection_monitoring_artifact.sh`. The isolated hash-locked package contract runs the package pytest configuration, whose 100% statement and branch coverage thresholds remain authoritative. `test_quality_workflow_trigger.py` fails if the retired leaf workflow reappears or this canonical delegation disappears. | There is no package-local Selection Monitoring workflow and no transfer of historical GREEN across a successor head | ## Executable evidence -`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth, repeated `__post_init__()` cannot overwrite the original construction seal, and missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py` proves fixed governance fields reject hostile runtime string subclasses before equality-based policy checks. `packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py` proves tenant, reference, purpose, reason, and SHA-256 digest evidence reject caller-defined string subclasses before canonical evidence binding. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `packages/selection-monitoring/tests/test_quality_workflow_trigger.py` proves the retired package-local workflow stays absent and canonical Foundation ownership continues to execute the package contract. +`packages/selection-monitoring/tests/test_plan.py` exercises direct-constructor and builder validation, operational tenant identity, UUID-backed reference namespaces, SHA-256 digests, requester/reviewer syntactic separation, monitoring-window boundaries, governance codes, timezone handling, fractional-second evidence identity, immutable review/authority state, aggregate-only enforcement, canonical JSON, and deterministic packet hashing. `packages/selection-monitoring/tests/test_temporal_evidence_integrity.py` proves caller-defined datetime subclasses are rejected, mutable timezone providers are detached at issuance, future generation times and missing/raising offsets fail closed, UTC-normalization overflow is normalized to validation failure, and post-construction non-UTC reinjection is rejected before evidence export. `packages/selection-monitoring/tests/test_issuance_integrity.py` proves a low-level valid-value rewrite after issuance cannot emit a second canonical truth, repeated `__post_init__()` cannot overwrite the original construction seal, and missing process-local issuance evidence fails closed. `packages/selection-monitoring/tests/test_fixed_governance_runtime_integrity.py` proves fixed governance fields reject hostile runtime string subclasses before equality-based policy checks. `packages/selection-monitoring/tests/test_string_runtime_evidence_integrity.py` proves tenant, reference, purpose, reason, and SHA-256 digest evidence reject caller-defined string subclasses before canonical evidence binding. `packages/selection-monitoring/tests/test_actor_separation.py` requires the immutable next action to re-resolve every packet reference in the exact tenant before Job-scope/accountable-review use, and separately requires requester/reviewer resolution through the authoritative tenant-scoped actor boundary with distinct resolved identities. `packages/selection-monitoring/tests/test_reference_privacy.py` is the RED→GREEN privacy/interoperability contract for accepting the authoritative core UUIDv7 tenant form while rejecting human-readable/value-bearing, sentinel, noncanonical, and non-v4 packet-owned opaque-reference suffixes through both public construction and replacement paths. `packages/selection-monitoring/tests/test_evidence_version.py` requires explicit bounded evidence revision identity in canonical evidence and proves that version changes alter the packet hash. `packages/selection-monitoring/tests/test_quality_workflow_trigger.py` proves the retired package-local workflow stays absent and canonical Foundation ownership continues to discover and execute the package contract without another edit to the shared Foundation workflow. -The package contract is reached only through canonical `.github/workflows/foundation-ci.yml`. It creates an isolated virtual environment, installs the repository's hash-locked reviewed test toolchain, runs the selection-monitoring package under its own pytest configuration, and therefore preserves the existing 100% owned statement/branch threshold without restoring `.github/workflows/selection-monitoring-quality.yml`. Organization-required central review/security workflows remain separate required controls. +The package contract is reached only through canonical `.github/workflows/foundation-ci.yml`. `tests/test_foundation_ci_dependency_hygiene.sh` discovers delegated artifact contracts by the `tests/test_foundation_ci_*_artifact.sh` naming contract, so later packages can add an isolated artifact proof without competing edits to the shared dispatcher. The Selection Monitoring contract creates an isolated virtual environment, installs the repository's hash-locked reviewed test toolchain, compiles the package and tests, runs the selection-monitoring package under its own pytest configuration, and removes the temporary environment on exit. This preserves the existing 100% owned statement/branch threshold without restoring `.github/workflows/selection-monitoring-quality.yml`. Organization-required central review/security workflows remain separate required controls. ## Ownership boundary diff --git a/packages/selection-monitoring/tests/test_quality_workflow_trigger.py b/packages/selection-monitoring/tests/test_quality_workflow_trigger.py index 711f92042..1b38326ed 100644 --- a/packages/selection-monitoring/tests/test_quality_workflow_trigger.py +++ b/packages/selection-monitoring/tests/test_quality_workflow_trigger.py @@ -6,7 +6,7 @@ _REPOSITORY_ROOT = Path(__file__).resolve().parents[3] _FOUNDATION_WORKFLOW = _REPOSITORY_ROOT / ".github/workflows/foundation-ci.yml" _DEPENDENCY_HYGIENE = _REPOSITORY_ROOT / "tests/test_foundation_ci_dependency_hygiene.sh" -_ARTIFACT_CONTRACT = _REPOSITORY_ROOT / "tests/test_selection_monitoring_artifact.sh" +_ARTIFACT_CONTRACT = _REPOSITORY_ROOT / "tests/test_foundation_ci_selection_monitoring_artifact.sh" _RETIRED_WORKFLOW = _REPOSITORY_ROOT / ".github/workflows/selection-monitoring-quality.yml" _PYPROJECT = _REPOSITORY_ROOT / "packages/selection-monitoring/pyproject.toml" @@ -21,15 +21,15 @@ def test_retired_selection_monitoring_workflow_stays_deleted() -> None: def test_foundation_owns_selection_monitoring_quality_contract() -> None: - """Require canonical Foundation CI to execute the package's exact coverage contract.""" + """Require canonical Foundation CI to discover and execute the package contract.""" foundation = _read(_FOUNDATION_WORKFLOW) hygiene = _read(_DEPENDENCY_HYGIENE) artifact = _read(_ARTIFACT_CONTRACT) pyproject = _read(_PYPROJECT) assert "bash tests/test_foundation_ci_dependency_hygiene.sh" in foundation - assert 'selection_monitoring_contract="${repository_root}/tests/test_selection_monitoring_artifact.sh"' in hygiene - assert 'bash "${selection_monitoring_contract}"' in hygiene + assert '"${repository_root}"/tests/test_foundation_ci_*_artifact.sh' in hygiene + assert 'bash "${artifact_contract}"' in hygiene assert 'retired_workflow="${repository_root}/.github/workflows/selection-monitoring-quality.yml"' in artifact assert 'PYTHONPATH="${package_root}/src"' in artifact assert '-c "${package_root}/pyproject.toml"' in artifact diff --git a/tests/test_foundation_ci_dependency_hygiene.sh b/tests/test_foundation_ci_dependency_hygiene.sh index 9e678d4be..c069a7958 100755 --- a/tests/test_foundation_ci_dependency_hygiene.sh +++ b/tests/test_foundation_ci_dependency_hygiene.sh @@ -4,7 +4,6 @@ set -euo pipefail repository_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" workflow_path="${repository_root}/.github/workflows/foundation-ci.yml" requirements_path="${repository_root}/.github/requirements/foundation-test.txt" -selection_monitoring_contract="${repository_root}/tests/test_selection_monitoring_artifact.sh" expected_install="python -m pip install --require-hashes --no-deps --only-binary=:all: -r .github/requirements/foundation-test.txt" expected_default_pr_target=$' pull_request:\n branches:\n - develop\n' @@ -92,9 +91,17 @@ for package_name in coverage iniconfig packaging pluggy Pygments pytest pytest-c fi done -if [[ ! -f "${selection_monitoring_contract}" ]]; then - printf 'Foundation CI Selection Monitoring artifact contract is missing.\n' >&2 +shopt -s nullglob +delegated_artifact_contracts=( + "${repository_root}"/tests/test_foundation_ci_*_artifact.sh +) +shopt -u nullglob + +if [[ "${#delegated_artifact_contracts[@]}" -eq 0 ]]; then + printf 'Foundation CI must own at least one delegated artifact contract.\n' >&2 exit 1 fi -bash "${selection_monitoring_contract}" +for artifact_contract in "${delegated_artifact_contracts[@]}"; do + bash "${artifact_contract}" +done diff --git a/tests/test_selection_monitoring_artifact.sh b/tests/test_foundation_ci_selection_monitoring_artifact.sh similarity index 86% rename from tests/test_selection_monitoring_artifact.sh rename to tests/test_foundation_ci_selection_monitoring_artifact.sh index dee90539b..a2ae1a8c8 100755 --- a/tests/test_selection_monitoring_artifact.sh +++ b/tests/test_foundation_ci_selection_monitoring_artifact.sh @@ -13,9 +13,15 @@ if [[ -e "${retired_workflow}" ]]; then fi rm -rf "${venv_dir}" +cleanup() { + rm -rf "${venv_dir}" +} +trap cleanup EXIT + python -m venv "${venv_dir}" "${venv_dir}/bin/python" -m pip install --require-hashes --no-deps --only-binary=:all: -r "${requirements_path}" "${venv_dir}/bin/python" -m pip check +"${venv_dir}/bin/python" -m compileall -q "${package_root}/src" "${package_root}/tests" PYTHONPATH="${package_root}/src" \ COVERAGE_FILE=/tmp/orgmetra-selection-monitoring.coverage \