From 81982e832e77306da116492421d9e7a45ce44d2e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:34:01 -0700 Subject: [PATCH 01/15] test(job-analysis): define qualification-rule review package --- .../pyproject.toml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 packages/job-qualification-rule-review/pyproject.toml diff --git a/packages/job-qualification-rule-review/pyproject.toml b/packages/job-qualification-rule-review/pyproject.toml new file mode 100644 index 000000000..44ce41587 --- /dev/null +++ b/packages/job-qualification-rule-review/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["setuptools>=69"] +build-backend = "setuptools.build_meta" + +[project] +name = "orgmetra-job-qualification-rule-review" +version = "0.1.0" +description = "Governed Job qualification-rule review 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_job_qualification_rule_review", + "--cov-branch", + "--cov-report=term-missing", + "--cov-fail-under=100", +] From 20f05a062c6b706d4387169827450f0d394e70a9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:34:09 -0700 Subject: [PATCH 02/15] test(job-analysis): expose qualification-rule review contract --- .../__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/__init__.py diff --git a/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/__init__.py b/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/__init__.py new file mode 100644 index 000000000..a54edce8a --- /dev/null +++ b/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/__init__.py @@ -0,0 +1,11 @@ +"""Public contract for governed Job qualification-rule review evidence.""" + +from .review import ( + JobQualificationRuleReviewPacket, + build_job_qualification_rule_review_packet, +) + +__all__ = [ + "JobQualificationRuleReviewPacket", + "build_job_qualification_rule_review_packet", +] From 55489124b6fa45dfeaa6dad33e54a08d916599c2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:34:49 -0700 Subject: [PATCH 03/15] test(job-analysis): add governed qualification-rule regressions --- .../tests/test_review.py | 354 ++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 packages/job-qualification-rule-review/tests/test_review.py diff --git a/packages/job-qualification-rule-review/tests/test_review.py b/packages/job-qualification-rule-review/tests/test_review.py new file mode 100644 index 000000000..f77793342 --- /dev/null +++ b/packages/job-qualification-rule-review/tests/test_review.py @@ -0,0 +1,354 @@ +"""Executable contract for governed Job qualification-rule review evidence.""" + +from dataclasses import replace +from datetime import date, datetime, timedelta, timezone +from decimal import Decimal +from uuid import uuid4 + +import pytest + +from orgmetra_job_qualification_rule_review import ( + JobQualificationRuleReviewPacket, + build_job_qualification_rule_review_packet, +) + + +DIGEST_A = "a" * 64 +DIGEST_B = "b" * 64 +DIGEST_C = "c" * 64 +DIGEST_D = "d" * 64 +TENANT_ID = "0198f1c0-7d6e-7f10-8a41-b1d9e2fe0199" +JOB_ID = str(uuid4()) +SNAPSHOT_ID = str(uuid4()) +RULE_ARTIFACT_ID = str(uuid4()) +REQUESTER_ID = str(uuid4()) +REVIEWER_ID = str(uuid4()) +REVIEWED_AT = datetime(2026, 8, 23, 20, 0, tzinfo=timezone.utc) + + +def values() -> dict[str, object]: + """Return one complete valid packet input for focused tests.""" + return { + "tenant_record_id": TENANT_ID, + "job_record_reference": f"job_record:{JOB_ID}", + "job_analysis_snapshot_reference": f"job_analysis_snapshot:{SNAPSHOT_ID}", + "job_analysis_snapshot_digest": DIGEST_A, + "qualification_rule_artifact_reference": f"qualification_rule_artifact:{RULE_ARTIFACT_ID}", + "qualification_rule_artifact_digest": DIGEST_B, + "task_linkage_digest": DIGEST_C, + "ksao_linkage_digest": DIGEST_D, + "source_evidence_digest": "e" * 64, + "rule_category": "knowledge_skill_ability_requirement", + "effective_on": date(2026, 9, 1), + "requester_actor_reference": f"actor:{REQUESTER_ID}", + "reviewer_actor_reference": f"actor:{REVIEWER_ID}", + "reason_code": "new_job_analysis", + "evidence_version": 1, + "reviewed_at": REVIEWED_AT, + } + + +def test_builds_human_reviewed_non_authoritative_qualification_rule_evidence() -> None: + """A valid packet binds reviewed job evidence without granting decision authority.""" + packet = build_job_qualification_rule_review_packet(**values()) + + assert isinstance(packet, JobQualificationRuleReviewPacket) + assert packet.purpose_code == "job_qualification_rule_review" + assert packet.review_state == "reviewed_for_authoritative_resolution" + assert packet.decision_authority == "not_authorized_for_candidate_or_employment_decision" + assert packet.human_review_required is True + assert packet.recorded_at >= packet.reviewed_at + assert packet.recorded_at.tzinfo is timezone.utc + assert "authoritative" in packet.next_action + assert "audit/outbox" in packet.next_action + assert repr(packet) == "JobQualificationRuleReviewPacket()" + + document = packet.canonical_document() + assert document["rule_category"] == "knowledge_skill_ability_requirement" + assert document["effective_on"] == "2026-09-01" + assert document["reviewed_at"] == "2026-08-23T20:00:00Z" + assert document["recorded_at"].endswith("Z") + assert len(packet.sha256_digest()) == 64 + + +def test_canonical_evidence_excludes_candidate_pii_rule_text_and_decision_values() -> None: + """Durable evidence must remain minimized to correlations, categories, and digests.""" + encoded = JobQualificationRuleReviewPacket(**values()).canonical_json() + + for forbidden in ( + "candidate_profile", + "person_record", + "name", + "email", + "phone", + "salary", + "assessment_score", + "cut_score", + "rule_text", + "qualification_text", + "prompt", + "model_output", + ): + assert forbidden not in encoded + + +@pytest.mark.parametrize( + "field,value", + [ + ("rule_category", "candidate_quality"), + ("rule_category", "license:CPA"), + ("reason_code", "manager_preference"), + ("reason_code", "candidate_failed"), + ], +) +def test_rejects_unreviewed_rule_categories_and_reasons(field: str, value: object) -> None: + """Qualification metadata must stay inside reviewed non-sensitive vocabularies.""" + data = values() + data[field] = value + with pytest.raises(ValueError): + JobQualificationRuleReviewPacket(**data) + + +def test_accepts_each_reviewed_rule_category_and_reason() -> None: + """Every published controlled category and reason remains executable.""" + categories = ( + "credential_requirement", + "education_training_requirement", + "experience_requirement", + "knowledge_skill_ability_requirement", + "task_or_work_requirement", + ) + reasons = ( + "new_job_analysis", + "job_analysis_revision", + "periodic_job_analysis_review", + "source_evidence_change", + ) + for category in categories: + data = values() + data["rule_category"] = category + assert JobQualificationRuleReviewPacket(**data).rule_category == category + for reason in reasons: + data = values() + data["reason_code"] = reason + assert JobQualificationRuleReviewPacket(**data).reason_code == reason + + +def test_rejects_requester_reviewer_overlap() -> None: + """The accountable human reviewer must not be the requesting actor correlation.""" + data = values() + data["reviewer_actor_reference"] = data["requester_actor_reference"] + with pytest.raises(ValueError, match="different actor"): + JobQualificationRuleReviewPacket(**data) + + +@pytest.mark.parametrize( + "field,value", + [ + ("tenant_record_id", "00000000-0000-0000-0000-000000000000"), + ("tenant_record_id", "ffffffff-ffff-ffff-ffff-ffffffffffff"), + ("tenant_record_id", "not-a-uuid"), + ("job_record_reference", f"position_record:{JOB_ID}"), + ("job_record_reference", "job_record:00000000-0000-0000-0000-000000000000"), + ("job_analysis_snapshot_reference", f"job_analysis:{SNAPSHOT_ID}"), + ("qualification_rule_artifact_reference", f"qualification_rule:{RULE_ARTIFACT_ID}"), + ( + "qualification_rule_artifact_reference", + "qualification_rule_artifact:6ba7b810-9dad-11d1-80b4-00c04fd430c8", + ), + ], +) +def test_rejects_invalid_scope_and_evidence_references(field: str, value: object) -> None: + """Authoritative scope and packet-owned evidence references must stay opaque and canonical.""" + data = values() + data[field] = value + with pytest.raises(ValueError): + JobQualificationRuleReviewPacket(**data) + + +@pytest.mark.parametrize( + "field", + [ + "qualification_rule_artifact_reference", + "requester_actor_reference", + "reviewer_actor_reference", + ], +) +def test_packet_owned_references_require_uuid4(field: str) -> None: + """Packet-owned correlations reject UUIDv1 timestamp/node metadata.""" + data = values() + prefix = str(data[field]).split(":", 1)[0] + data[field] = f"{prefix}:6ba7b810-9dad-11d1-80b4-00c04fd430c8" + with pytest.raises(ValueError, match="UUIDv4"): + JobQualificationRuleReviewPacket(**data) + + +@pytest.mark.parametrize( + "field,value", + [ + ("job_analysis_snapshot_digest", "A" * 64), + ("qualification_rule_artifact_digest", "abc"), + ("task_linkage_digest", "g" * 64), + ("ksao_linkage_digest", 7), + ("source_evidence_digest", "f" * 63), + ], +) +def test_rejects_noncanonical_sha256_evidence(field: str, value: object) -> None: + """Every provenance digest must be exact lower-case SHA-256 text.""" + data = values() + data[field] = value + with pytest.raises(ValueError): + JobQualificationRuleReviewPacket(**data) + + +class ForgedText(str): + """Hostile string subtype that attempts to forge equality and hashing.""" + + def __eq__(self, other: object) -> bool: + """Pretend to equal any compared governance value.""" + return True + + def __hash__(self) -> int: + """Return a forged stable hash.""" + return 0 + + +@pytest.mark.parametrize( + "field", + [ + "tenant_record_id", + "job_record_reference", + "job_analysis_snapshot_reference", + "qualification_rule_artifact_reference", + "job_analysis_snapshot_digest", + "task_linkage_digest", + "rule_category", + "requester_actor_reference", + "reason_code", + ], +) +def test_rejects_hostile_string_subclasses_before_governance_checks(field: str) -> None: + """Trust-bearing text cannot execute caller-defined equality, hash, or parser behavior.""" + data = values() + data[field] = ForgedText(str(data[field])) + with pytest.raises(ValueError): + JobQualificationRuleReviewPacket(**data) + + +class ForgedDate(date): + """Hostile date subtype that must not enter the business-time boundary.""" + + +def test_effective_date_requires_exact_builtin_date() -> None: + """Business-effective time rejects datetime and caller-defined date behavior.""" + for value in ( + datetime(2026, 9, 1, tzinfo=timezone.utc), + ForgedDate(2026, 9, 1), + "2026-09-01", + ): + data = values() + data["effective_on"] = value + with pytest.raises(ValueError, match="effective_on"): + JobQualificationRuleReviewPacket(**data) + + +class ForgedDateTime(datetime): + """Hostile datetime subtype that must not enter accountable review time.""" + + +def test_review_time_requires_exact_builtin_utc_datetime() -> None: + """Human-review evidence rejects naive, offset, and caller-defined datetimes.""" + candidates = ( + datetime(2026, 8, 23, 20, 0), + datetime(2026, 8, 24, 5, 0, tzinfo=timezone(timedelta(hours=9))), + ForgedDateTime(2026, 8, 23, 20, 0, tzinfo=timezone.utc), + ) + for value in candidates: + data = values() + data["reviewed_at"] = value + with pytest.raises(ValueError, match="reviewed_at"): + JobQualificationRuleReviewPacket(**data) + + +def test_future_human_review_cannot_precede_owner_generated_system_time() -> None: + """A caller cannot claim a human review that occurs after owner-generated issuance.""" + data = values() + data["reviewed_at"] = datetime.now(timezone.utc) + timedelta(days=1) + with pytest.raises(ValueError, match="recorded_at"): + JobQualificationRuleReviewPacket(**data) + + +@pytest.mark.parametrize("value", [True, 0, -1, 2_147_483_648, "1", Decimal("1")]) +def test_evidence_version_requires_bounded_exact_integer(value: object) -> None: + """High-impact evidence versioning rejects bool, non-int, nonpositive, and overflow values.""" + data = values() + data["evidence_version"] = value + with pytest.raises(ValueError, match="evidence_version"): + JobQualificationRuleReviewPacket(**data) + + +def test_higher_valid_evidence_version_changes_canonical_evidence() -> None: + """A reviewed evidence revision must be explicit in canonical audit correlation.""" + first = JobQualificationRuleReviewPacket(**values()) + data = values() + data["evidence_version"] = 2 + second = JobQualificationRuleReviewPacket(**data) + assert second.canonical_document()["evidence_version"] == 2 + assert first.sha256_digest() != second.sha256_digest() + + +@pytest.mark.parametrize( + "field,value", + [ + ("purpose_code", "candidate_screening"), + ("review_state", "auto_approved"), + ("decision_authority", "authorized_to_reject_candidate"), + ("human_review_required", False), + ("next_action", "reject candidate now"), + ], +) +def test_direct_construction_cannot_weaken_fixed_governance(field: str, value: object) -> None: + """Direct construction cannot convert review evidence into employment-decision authority.""" + data = values() + data[field] = value + with pytest.raises(ValueError): + JobQualificationRuleReviewPacket(**data) + + +def test_recorded_at_is_owner_generated_and_not_a_public_constructor_input() -> None: + """Callers cannot inject or backdate system-recorded issuance time.""" + data = values() + data["recorded_at"] = REVIEWED_AT + with pytest.raises(TypeError): + JobQualificationRuleReviewPacket(**data) + + +def test_post_issuance_valid_field_mutation_invalidates_all_exports() -> None: + """Even a syntactically valid field rewrite cannot create a second canonical truth.""" + packet = JobQualificationRuleReviewPacket(**values()) + object.__setattr__(packet, "rule_category", "experience_requirement") + + with pytest.raises(ValueError, match="modified after issuance"): + packet.canonical_document() + with pytest.raises(ValueError, match="modified after issuance"): + packet.canonical_json() + with pytest.raises(ValueError, match="modified after issuance"): + packet.sha256_digest() + + +def test_post_issuance_hostile_runtime_mutation_fails_before_serialization() -> None: + """Export revalidation rejects hostile runtime types before calling their text behavior.""" + packet = JobQualificationRuleReviewPacket(**values()) + object.__setattr__(packet, "rule_category", ForgedText("experience_requirement")) + with pytest.raises(ValueError, match="exact string"): + packet.canonical_json() + + +def test_replace_remains_non_authoritative_and_reissues_system_time() -> None: + """A copied proposal is independently issued and never inherits employment authority.""" + first = JobQualificationRuleReviewPacket(**values()) + second = replace(first, rule_category="experience_requirement") + + assert second.decision_authority == "not_authorized_for_candidate_or_employment_decision" + assert second.recorded_at >= first.recorded_at + assert second.sha256_digest() != first.sha256_digest() From 89e83ebde7f66e14b954d4cf361f65b810086138 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:35:03 -0700 Subject: [PATCH 04/15] test(job-analysis): gate qualification-rule review artifact --- .../job-qualification-rule-review-quality.yml | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 .github/workflows/job-qualification-rule-review-quality.yml diff --git a/.github/workflows/job-qualification-rule-review-quality.yml b/.github/workflows/job-qualification-rule-review-quality.yml new file mode 100644 index 000000000..1cb03275f --- /dev/null +++ b/.github/workflows/job-qualification-rule-review-quality.yml @@ -0,0 +1,95 @@ +name: Job Qualification Rule Review Quality + +on: + pull_request: + branches: + - develop + paths: + - "packages/job-qualification-rule-review/**" + - ".github/requirements/foundation-test.txt" + - ".github/workflows/job-qualification-rule-review-quality.yml" + - "docs/adr/0104-governed-job-qualification-rule-review.md" + - "docs/doctoring/job-qualification-rule-review-references.md" + - "docs/traceability/job-qualification-rule-review.md" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: job-qualification-rule-review-quality-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + unit: + name: Job qualification-rule review 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.7" + check-latest: false + - name: Install reviewed test and build toolchain + run: | + python -m pip install --require-hashes --no-deps --only-binary=:all: -r .github/requirements/foundation-test.txt + printf '%s\n' 'setuptools==84.0.0 --hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670' > /tmp/orgmetra-job-qualification-rule-review-build.txt + python -m pip install --require-hashes --no-deps --only-binary=:all: -r /tmp/orgmetra-job-qualification-rule-review-build.txt + python -m pip check + - name: Compile Job qualification-rule review package + run: python -m compileall -q packages/job-qualification-rule-review/src packages/job-qualification-rule-review/tests + - name: Build and install exact package artifact + run: | + rm -rf /tmp/orgmetra-job-qualification-rule-review-build-tree /tmp/orgmetra-job-qualification-rule-review-dist /tmp/orgmetra-job-qualification-rule-review-venv + cp -a packages/job-qualification-rule-review /tmp/orgmetra-job-qualification-rule-review-build-tree + mkdir -p /tmp/orgmetra-job-qualification-rule-review-dist + python -m pip wheel --no-deps --no-build-isolation --wheel-dir /tmp/orgmetra-job-qualification-rule-review-dist /tmp/orgmetra-job-qualification-rule-review-build-tree + test "$(find /tmp/orgmetra-job-qualification-rule-review-dist -maxdepth 1 -type f -name '*.whl' | wc -l)" -eq 1 + python -m venv /tmp/orgmetra-job-qualification-rule-review-venv + /tmp/orgmetra-job-qualification-rule-review-venv/bin/python -m pip install --require-hashes --no-deps --only-binary=:all: -r "$GITHUB_WORKSPACE/.github/requirements/foundation-test.txt" + wheel_path="$(find /tmp/orgmetra-job-qualification-rule-review-dist -maxdepth 1 -type f -name '*.whl' -print -quit)" + wheel_sha="$(sha256sum "$wheel_path" | awk '{print $1}')" + printf 'orgmetra-job-qualification-rule-review[test] @ file://%s --hash=sha256:%s\n' "$wheel_path" "$wheel_sha" > /tmp/orgmetra-job-qualification-rule-review-install.txt + /tmp/orgmetra-job-qualification-rule-review-venv/bin/python -m pip install --require-hashes --no-deps -r /tmp/orgmetra-job-qualification-rule-review-install.txt + /tmp/orgmetra-job-qualification-rule-review-venv/bin/python -m pip check + /tmp/orgmetra-job-qualification-rule-review-venv/bin/python - <<'PY' + from importlib.metadata import metadata + from pathlib import Path + import coverage + import pytest + import pytest_cov + import orgmetra_job_qualification_rule_review + + venv_root = Path("/tmp/orgmetra-job-qualification-rule-review-venv").resolve() + module_path = Path(orgmetra_job_qualification_rule_review.__file__).resolve() + if not module_path.is_relative_to(venv_root): + raise SystemExit(f"package imported outside isolated environment: {module_path}") + for module in (coverage, pytest, pytest_cov): + dependency_path = Path(module.__file__).resolve() + if not dependency_path.is_relative_to(venv_root): + raise SystemExit( + f"test dependency imported outside isolated environment: {dependency_path}" + ) + if "test" not in (metadata("orgmetra-job-qualification-rule-review").get_all("Provides-Extra") or []): + raise SystemExit("built distribution does not expose the reviewed test extra") + PY + - name: Test installed Job qualification-rule review artifact with exact statement and branch coverage + env: + COVERAGE_FILE: /tmp/orgmetra-job-qualification-rule-review.coverage + run: | + cd /tmp + /tmp/orgmetra-job-qualification-rule-review-venv/bin/python -m pytest -c "$GITHUB_WORKSPACE/packages/job-qualification-rule-review/pyproject.toml" "$GITHUB_WORKSPACE/packages/job-qualification-rule-review/tests" + - name: Require clean checkout + run: | + git diff --exit-code + test -z "$(git status --porcelain)" From 491f5f8e39e652281cb70c4102ce5dc5fe3e9753 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:35:14 -0700 Subject: [PATCH 05/15] docs(job-analysis): define qualification-rule review boundary --- .../job-qualification-rule-review/README.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/job-qualification-rule-review/README.md diff --git a/packages/job-qualification-rule-review/README.md b/packages/job-qualification-rule-review/README.md new file mode 100644 index 000000000..ccc4c526b --- /dev/null +++ b/packages/job-qualification-rule-review/README.md @@ -0,0 +1,30 @@ +# Orgmetra Job qualification-rule review + +This package creates **human-reviewed, evidence-linked Job qualification-rule proposals**. It does not screen a candidate, reject an applicant, assign a qualification outcome, or mutate authoritative Job or Job Analysis state. + +A packet binds one tenant and Job to an exact Job Analysis snapshot, an opaque reviewed qualification-rule artifact, explicit Task/KSAO/source evidence digests, one controlled rule category, a business-effective date, distinct requester/reviewer correlations, evidence version, human review time, and an Orgmetra-generated system-recorded issuance time. + +The canonical evidence deliberately excludes candidate/person identifiers, names, email addresses, phone numbers, salary, assessment scores, cut scores, raw qualification/rule text, prompts, model output, credentials, and free-form review text. The reviewed artifact content remains outside this evidence envelope and is correlated by opaque reference plus SHA-256 digest. + +## Controlled categories + +- `credential_requirement` +- `education_training_requirement` +- `experience_requirement` +- `knowledge_skill_ability_requirement` +- `task_or_work_requirement` + +These categories classify the *kind of reviewed job requirement evidence*. They do not establish that a particular qualification standard is lawful, necessary, validated, or satisfied by any person. + +## Safety boundary + +Every packet remains fixed to: + +- purpose `job_qualification_rule_review`; +- review state `reviewed_for_authoritative_resolution`; +- decision authority `not_authorized_for_candidate_or_employment_decision`; +- mandatory human review. + +Before any qualification rule affects recruiting or selection, the Orgmetra host must re-resolve tenant, Job, Job Analysis snapshot, exact artifact and linkage digests, accountable reviewer identity/authority, and the current business-time context. It must then persist authoritative changes with immutable audit/outbox evidence. Candidate evaluation is a separate governed boundary. + +System-recorded time is generated by this package rather than supplied by callers. Trust-bearing scalar values require exact runtime primitives. Canonical exports revalidate a single payload snapshot and compare it with an issuance digest held outside writable packet slots so post-issuance mutation fails closed. This process-local seal is defense in depth, not a durable signature or authorization mechanism. From 85883b609869b359a349e562d770e178397364b4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:35:22 -0700 Subject: [PATCH 06/15] docs(job-analysis): record qualification-rule review slice --- packages/job-qualification-rule-review/CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 packages/job-qualification-rule-review/CHANGELOG.md diff --git a/packages/job-qualification-rule-review/CHANGELOG.md b/packages/job-qualification-rule-review/CHANGELOG.md new file mode 100644 index 000000000..e3a3c09b3 --- /dev/null +++ b/packages/job-qualification-rule-review/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +## 0.1.0 - 2026-08-24 + +- Add governed Job qualification-rule review evidence linked to authoritative Job Analysis. +- Bind Task, KSAO, source, snapshot, and qualification-rule artifact provenance by SHA-256 without copying candidate PII or raw rule text. +- Keep rule proposals explicitly non-authoritative for applicant screening, candidate rejection, and employment decisions. +- Require distinct requester/reviewer correlations, controlled rule categories/reasons, explicit evidence versioning, business-effective time, human-review time, and owner-generated system-recorded time. +- Add deterministic canonical JSON/SHA-256 correlation, redacted representations, process-local post-issuance tamper detection, hostile runtime-type regressions, hash-bound isolated-wheel execution, and exact 100% owned statement/branch coverage. From 8e1358fa80517e754c4700a81268fad529059075 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:35:36 -0700 Subject: [PATCH 07/15] docs(job-analysis): add qualification-rule review ADR --- ...-governed-job-qualification-rule-review.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/adr/0104-governed-job-qualification-rule-review.md diff --git a/docs/adr/0104-governed-job-qualification-rule-review.md b/docs/adr/0104-governed-job-qualification-rule-review.md new file mode 100644 index 000000000..e1932c766 --- /dev/null +++ b/docs/adr/0104-governed-job-qualification-rule-review.md @@ -0,0 +1,33 @@ +# ADR 0104: Governed Job qualification-rule review + +- Status: proposed on active PR; not protected-main truth until merged. +- Date: 2026-08-24 + +## Context + +Protected `develop` models Job Analysis versions, Tasks, FJA and KSAO evidence, and the PRD requires qualification rules, but there is no bounded governance object proving which job-analysis evidence supported a proposed qualification rule before that rule can influence recruiting or selection. + +OPM describes job analysis as the systematic linkage of job tasks and competencies/KSAs and states that job-analysis information supports recruitment, qualification, assessment and selection. OPM also emphasizes evidence for task/KSA importance and current job requirements. The Uniform Guidelines on Employee Selection Procedures provide a federal framework for the proper use and documentation of selection procedures. Orgmetra uses these public sources as design evidence only; this package does not decide legal compliance, reproduce licensed standards, or validate a selection procedure. + +## Decision + +Create an Orgmetra-owned `JobQualificationRuleReviewPacket` as PII-minimized, human-reviewed proposal evidence. + +The packet binds tenant and authoritative Job scope, one Job Analysis snapshot reference/digest, one opaque qualification-rule artifact reference/digest, Task/KSAO/source linkage digests, a controlled rule category, a business-effective date, distinct requester/reviewer correlations, a controlled review reason, bounded evidence version, human review time, and an Orgmetra-generated system-recorded UTC issuance time. + +The controlled category describes only the type of job requirement evidence (`credential_requirement`, `education_training_requirement`, `experience_requirement`, `knowledge_skill_ability_requirement`, or `task_or_work_requirement`). It does not record candidate qualification, cut scores, assessment outcomes, legal status, or an autonomous eligibility decision. + +Every packet is fixed to human review and `not_authorized_for_candidate_or_employment_decision`. Before a reviewed rule changes authoritative Job/Job Analysis truth or is used in recruiting/selection, the host must re-resolve exact tenant/Job/snapshot/artifact/linkage evidence and reviewer authority at the relevant business-time coordinate and atomically preserve immutable audit/outbox evidence. + +## Privacy and integrity consequences + +Canonical evidence excludes Person/candidate identifiers and PII, compensation, assessment scores, raw rule or qualification text, credentials, prompts/model output, and free-form review text. Operational HRIS references use the core non-sentinel UUID contract; packet-owned artifact and actor correlations require opaque UUIDv4 references. + +Trust-bearing primitives are exact built-in types. Malformed/noncanonical IDs or SHA-256, unreviewed categories/reasons, actor overlap, invalid evidence versions, caller-supplied system time, future review time, mutable fixed governance, and post-issuance mutation fail closed. The process-local issuance registry is defense in depth only; durable identity, uniqueness, authorization, and audit remain host responsibilities. + +## Alternatives rejected + +- **Store free-form minimum-qualification text in the governance packet.** Rejected because it expands durable sensitive/business text without improving provenance; the reviewed artifact is referenced and hashed instead. +- **Evaluate candidates inside this packet.** Rejected because job-rule governance and candidate evidence/selection decisions are distinct high-impact boundaries. +- **Infer rules directly from an LLM.** Rejected because model output is untrusted draft evidence and cannot substitute for Job Analysis linkage or accountable human review. +- **Treat a qualification category as proof of validity or legality.** Rejected because jurisdiction, job relatedness, validity and applicant application require separate authoritative review. From 24904640a0d287de520f02ba8ed51ad829ae222c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:35:45 -0700 Subject: [PATCH 08/15] docs(job-analysis): record qualification-rule primary sources --- ...ob-qualification-rule-review-references.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/doctoring/job-qualification-rule-review-references.md diff --git a/docs/doctoring/job-qualification-rule-review-references.md b/docs/doctoring/job-qualification-rule-review-references.md new file mode 100644 index 000000000..e7f9a40ce --- /dev/null +++ b/docs/doctoring/job-qualification-rule-review-references.md @@ -0,0 +1,28 @@ +# Job qualification-rule review references + +Reviewed: 2026-08-24. + +## Authoritative public sources + +U.S. Office of Personnel Management. (n.d.). *Job analysis*. https://www.opm.gov/frequently-asked-questions/assessment-policy-faq/job-analysis/ + +- OPM describes job analysis as a systematic procedure for identifying work tasks and the competencies/KSAs directly related to performance. +- OPM states that job-analysis information supports recruitment, assessment, selection, performance management, and career development, and that evidence should establish the importance of tasks and KSAs. + +U.S. Office of Personnel Management. (n.d.). *General Schedule qualification policies*. https://www.opm.gov/policy-data-oversight/classification-qualifications/general-schedule-qualification-policies/ + +- OPM distinguishes job-analysis evidence from the ways applicants may demonstrate KSAs, including qualifying experience, education, or training. +- Orgmetra uses that distinction only to keep Job qualification-rule provenance separate from candidate evaluation; it does not adopt federal qualification standards as a universal employer policy. + +Equal Employment Opportunity Commission, Department of Justice, Department of Labor, & Civil Service Commission. (1978). *Uniform Guidelines on Employee Selection Procedures*, 29 C.F.R. pt. 1607. https://www.govinfo.gov/content/pkg/CFR-2023-title29-vol4/pdf/CFR-2023-title29-vol4-part1607.pdf + +- The Guidelines provide a federal framework for proper use, validity evidence, and documentation of employee selection procedures. +- This package records review provenance only. It makes no finding that a qualification rule is legally required, job-related, validated, nondiscriminatory, or appropriate for a particular jurisdiction or applicant. + +## Repository sources of truth + +ContextualWisdomLab. (2026). *Orgmetra product requirements document*. `docs/PRD.md` on protected `develop`. + +ContextualWisdomLab. (2026). *Orgmetra Job Analysis kernel*. `packages/hris-kernel/src/orgmetra_hris_kernel/job_analysis.py` on protected `develop`. + +The protected-main sources remain authoritative for Job/Job Analysis ownership. This active PR adds review evidence only and does not create a second source of Job truth or a candidate-screening engine. From f06498079027b0f381beaf500ba7487ddcd6e555 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:35:56 -0700 Subject: [PATCH 09/15] docs(job-analysis): trace qualification-rule governance --- .../job-qualification-rule-review.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/traceability/job-qualification-rule-review.md diff --git a/docs/traceability/job-qualification-rule-review.md b/docs/traceability/job-qualification-rule-review.md new file mode 100644 index 000000000..fee6265de --- /dev/null +++ b/docs/traceability/job-qualification-rule-review.md @@ -0,0 +1,29 @@ +# Job qualification-rule review traceability + +## State boundary + +- **Protected-main truth:** `develop@9e3e4847510e1e612b48474ba42b177b8ed824df` has authoritative Job Analysis Task/FJA/KSAO evidence but no dedicated governed qualification-rule review packet. +- **Active PR truth:** this branch proposes `JobQualificationRuleReviewPacket`; it is not protected-main truth until merged. +- **Foreign dependencies:** none are written or queried directly. This slice is Orgmetra-owned and standalone. + +## Requirement-to-evidence matrix + +| Requirement | Production boundary | Regression / gate | +|---|---|---| +| Tie proposed qualification rules to authoritative Job Analysis | Job + Job Analysis snapshot reference/digest | scope-ID and digest regressions | +| Preserve Task/KSAO/source provenance | independent Task, KSAO and source SHA-256 digests | malformed digest regressions | +| Avoid free-form sensitive rule storage | opaque qualification-rule artifact reference + digest and closed rule category | canonical privacy assertions | +| Keep job-rule design separate from candidate evaluation | fixed `not_authorized_for_candidate_or_employment_decision` | fixed-governance direct-construction tests | +| Preserve authoritative identifier ownership | tenant/Job/snapshot use non-sentinel operational UUIDs; packet-owned artifact/actors use UUIDv4 | namespace/sentinel/version regressions | +| Preserve business, review and system-recorded time separately | exact `effective_on`, exact UTC `reviewed_at`, owner-generated UTC `recorded_at` | type/timezone/future-review/caller-injection regressions | +| Require accountable human review | distinct requester/reviewer UUIDv4 correlations and mandatory review state | actor-overlap and governance regressions | +| Version high-impact evidence | bounded exact integer `evidence_version` | bool/non-int/range and digest-change regressions | +| Detect in-process post-issuance mutation | process-local weak issuance digest outside packet slots; export revalidates one snapshot | valid-field and hostile-runtime mutation regressions | +| Preserve exact package/artifact evidence | dedicated exact-head workflow builds and SHA-256-binds isolated wheel | Job Qualification Rule Review Quality | +| Preserve exact owned test sufficiency | package pytest config requires statement and branch coverage 100% | `--cov-fail-under=100`, branch coverage enabled | + +## Authoritative next action + +Before a reviewed rule may alter Job/Job Analysis truth or influence applicant screening or selection, the Orgmetra host must re-resolve the exact tenant, Job, Job Analysis snapshot, qualification-rule artifact, Task/KSAO/source evidence, reviewer identity and authority, and current business-time context. Any authoritative change must preserve human confirmation and immutable audit/outbox evidence atomically. + +The packet does not decide whether an applicant satisfies a rule, whether a rule is lawful or validated, or whether any employment action should occur. LLM output remains untrusted draft evidence only. From f48c23e953b35201818b5aeb6649ec56932328ef Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:36:53 -0700 Subject: [PATCH 10/15] feat(job-analysis): implement governed qualification-rule review --- .../review.py | 358 ++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py diff --git a/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py b/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py new file mode 100644 index 000000000..0caa7fcc8 --- /dev/null +++ b/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py @@ -0,0 +1,358 @@ +"""Governed, PII-minimized Job qualification-rule review evidence. + +This module records a human-reviewed proposal tying one qualification-rule artifact to +an authoritative Job Analysis snapshot and its Task/KSAO/source provenance. It never +evaluates a candidate, rejects an applicant, mutates Job truth, or authorizes an +employment decision. Authoritative use must re-resolve the reviewed evidence and +human authority before persisting any rule or applying it to a person. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from datetime import date, datetime, timezone +from hashlib import sha256 +import json +import re +from threading import RLock +from typing import ClassVar +from uuid import UUID +from weakref import WeakKeyDictionary + +_DIGEST_PATTERN = re.compile(r"^[0-9a-f]{64}$") +_MAX_UUID_INT = (1 << 128) - 1 +_MAX_EVIDENCE_VERSION = 2_147_483_647 +_PURPOSE_CODE = "job_qualification_rule_review" +_REVIEW_STATE = "reviewed_for_authoritative_resolution" +_DECISION_AUTHORITY = "not_authorized_for_candidate_or_employment_decision" +_ALLOWED_RULE_CATEGORIES = frozenset( + { + "credential_requirement", + "education_training_requirement", + "experience_requirement", + "knowledge_skill_ability_requirement", + "task_or_work_requirement", + } +) +_ALLOWED_REASON_CODES = frozenset( + { + "new_job_analysis", + "job_analysis_revision", + "periodic_job_analysis_review", + "source_evidence_change", + } +) +_NEXT_ACTION = ( + "Within tenant_record_id, re-resolve the authoritative Job and Job Analysis snapshot, " + "verify the exact qualification-rule artifact and Task/KSAO/source evidence digests, " + "confirm accountable reviewer identity and authority at the business-effective coordinate, " + "then persist any authoritative Job-rule change with immutable audit/outbox evidence. " + "Candidate evaluation and employment decisions require separate governed human review." +) + + +def _require_exact_text(value: object, field_name: str) -> str: + """Return trust-bearing text only when it is an exact built-in string.""" + if type(value) is not str: + raise ValueError(f"{field_name} must be an exact string") + return value + + +def _validate_operational_uuid_text(value: object, field_name: str) -> str: + """Require canonical non-sentinel UUID text without imposing one UUID version.""" + text = _require_exact_text(value, field_name) + try: + parsed = UUID(text) + except ValueError as exc: + raise ValueError(f"{field_name} must be canonical operational UUID text") from exc + if str(parsed) != text or parsed.int in (0, _MAX_UUID_INT): + raise ValueError(f"{field_name} must be canonical operational UUID text") + return text + + +def _validate_reference( + value: object, + prefix: str, + field_name: str, + *, + require_uuid4: bool, +) -> str: + """Require a namespaced canonical UUID reference owned by the stated boundary.""" + text = _require_exact_text(value, field_name) + namespace = f"{prefix}:" + if len(text) > 160 or not text.startswith(namespace): + raise ValueError(f"{field_name} must be an opaque {prefix}: reference") + suffix = text[len(namespace) :] + try: + parsed = UUID(suffix) + except ValueError as exc: + raise ValueError( + f"{field_name} must be an opaque {prefix}: reference" + ) from exc + if str(parsed) != suffix or parsed.int in (0, _MAX_UUID_INT): + raise ValueError( + f"{field_name} must be an opaque {prefix}: reference" + ) + if require_uuid4 and parsed.version != 4: + raise ValueError(f"{field_name} must use an opaque canonical UUIDv4 reference") + return text + + +def _validate_digest(value: object, field_name: str) -> str: + """Require exact lower-case SHA-256 hexadecimal evidence.""" + text = _require_exact_text(value, field_name) + if not _DIGEST_PATTERN.fullmatch(text): + raise ValueError(f"{field_name} must be lower-case SHA-256 hex") + return text + + +def _validate_rule_category(value: object) -> str: + """Require one reviewed non-sensitive qualification-rule category.""" + text = _require_exact_text(value, "rule_category") + if text not in _ALLOWED_RULE_CATEGORIES: + raise ValueError("rule_category must use a reviewed Job qualification category") + return text + + +def _validate_reason_code(value: object) -> str: + """Require one reviewed non-sensitive Job-analysis review reason.""" + text = _require_exact_text(value, "reason_code") + if text not in _ALLOWED_REASON_CODES: + raise ValueError("reason_code must use a reviewed Job-analysis reason") + return text + + +def _validate_effective_date(value: object) -> date: + """Require an exact built-in date for business-effective rule semantics.""" + if type(value) is not date: + raise ValueError("effective_on must be an exact built-in date") + return value + + +def _validate_utc_timestamp(value: object, field_name: str) -> datetime: + """Require an exact built-in datetime whose timezone is the UTC singleton.""" + if type(value) is not datetime or value.tzinfo is not timezone.utc: + raise ValueError(f"{field_name} must be an exact built-in UTC datetime") + return value + + +def _validate_evidence_version(value: object) -> int: + """Require one bounded positive exact integer evidence version.""" + if type(value) is not int or not 1 <= value <= _MAX_EVIDENCE_VERSION: + raise ValueError("evidence_version must be an exact integer from 1 through 2147483647") + return value + + +def _canonical_timestamp(value: datetime) -> str: + """Render a previously validated UTC datetime as deterministic RFC 3339 text.""" + return value.isoformat().replace("+00:00", "Z") + + +def _canonical_json(payload: dict[str, object]) -> str: + """Serialize one evidence payload deterministically for audit correlation.""" + return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + + +@dataclass(frozen=True, slots=True, weakref_slot=True, eq=False, repr=False) +class JobQualificationRuleReviewPacket: + """Human-reviewed Job qualification-rule proposal without candidate-decision authority.""" + + tenant_record_id: str + job_record_reference: str + job_analysis_snapshot_reference: str + job_analysis_snapshot_digest: str + qualification_rule_artifact_reference: str + qualification_rule_artifact_digest: str + task_linkage_digest: str + ksao_linkage_digest: str + source_evidence_digest: str + rule_category: str + effective_on: date + requester_actor_reference: str + reviewer_actor_reference: str + reason_code: str + evidence_version: int + reviewed_at: datetime + recorded_at: datetime = field(init=False) + purpose_code: str = _PURPOSE_CODE + review_state: str = _REVIEW_STATE + decision_authority: str = _DECISION_AUTHORITY + human_review_required: bool = True + next_action: str = _NEXT_ACTION + + _issuance_digests: ClassVar[ + WeakKeyDictionary["JobQualificationRuleReviewPacket", str] + ] = WeakKeyDictionary() + _issuance_lock: ClassVar[RLock] = RLock() + + def __post_init__(self) -> None: + """Generate system time, validate all trust-bearing fields, and seal evidence.""" + object.__setattr__(self, "recorded_at", datetime.now(timezone.utc)) + payload = self._validated_payload() + digest = sha256(_canonical_json(payload).encode("utf-8")).hexdigest() + with self._issuance_lock: + self._issuance_digests[self] = digest + + def __repr__(self) -> str: + """Return a fully redacted representation safe for routine logs and assertions.""" + return "JobQualificationRuleReviewPacket()" + + def _validated_payload(self) -> dict[str, object]: + """Revalidate and snapshot every canonical evidence field exactly once.""" + tenant_record_id = _validate_operational_uuid_text( + self.tenant_record_id, "tenant_record_id" + ) + job_record_reference = _validate_reference( + self.job_record_reference, + "job_record", + "job_record_reference", + require_uuid4=False, + ) + job_analysis_snapshot_reference = _validate_reference( + self.job_analysis_snapshot_reference, + "job_analysis_snapshot", + "job_analysis_snapshot_reference", + require_uuid4=False, + ) + job_analysis_snapshot_digest = _validate_digest( + self.job_analysis_snapshot_digest, "job_analysis_snapshot_digest" + ) + qualification_rule_artifact_reference = _validate_reference( + self.qualification_rule_artifact_reference, + "qualification_rule_artifact", + "qualification_rule_artifact_reference", + require_uuid4=True, + ) + qualification_rule_artifact_digest = _validate_digest( + self.qualification_rule_artifact_digest, + "qualification_rule_artifact_digest", + ) + task_linkage_digest = _validate_digest(self.task_linkage_digest, "task_linkage_digest") + ksao_linkage_digest = _validate_digest(self.ksao_linkage_digest, "ksao_linkage_digest") + source_evidence_digest = _validate_digest( + self.source_evidence_digest, "source_evidence_digest" + ) + rule_category = _validate_rule_category(self.rule_category) + effective_on = _validate_effective_date(self.effective_on) + requester_actor_reference = _validate_reference( + self.requester_actor_reference, + "actor", + "requester_actor_reference", + require_uuid4=True, + ) + reviewer_actor_reference = _validate_reference( + self.reviewer_actor_reference, + "actor", + "reviewer_actor_reference", + require_uuid4=True, + ) + if requester_actor_reference == reviewer_actor_reference: + raise ValueError("requester and reviewer must be different actor references") + reason_code = _validate_reason_code(self.reason_code) + evidence_version = _validate_evidence_version(self.evidence_version) + reviewed_at = _validate_utc_timestamp(self.reviewed_at, "reviewed_at") + recorded_at = _validate_utc_timestamp(self.recorded_at, "recorded_at") + if recorded_at < reviewed_at: + raise ValueError("recorded_at cannot precede reviewed_at") + if type(self.purpose_code) is not str or self.purpose_code != _PURPOSE_CODE: + raise ValueError("purpose_code must remain job_qualification_rule_review") + if type(self.review_state) is not str or self.review_state != _REVIEW_STATE: + raise ValueError("review_state must remain reviewed_for_authoritative_resolution") + if ( + type(self.decision_authority) is not str + or self.decision_authority != _DECISION_AUTHORITY + ): + raise ValueError( + "decision_authority must remain not_authorized_for_candidate_or_employment_decision" + ) + if self.human_review_required is not True: + raise ValueError("human review is mandatory for Job qualification-rule evidence") + if type(self.next_action) is not str or self.next_action != _NEXT_ACTION: + raise ValueError("next_action must remain the governed qualification-rule instruction") + + return { + "decision_authority": self.decision_authority, + "effective_on": effective_on.isoformat(), + "evidence_version": evidence_version, + "human_review_required": self.human_review_required, + "job_analysis_snapshot_digest": job_analysis_snapshot_digest, + "job_analysis_snapshot_reference": job_analysis_snapshot_reference, + "job_record_reference": job_record_reference, + "ksao_linkage_digest": ksao_linkage_digest, + "next_action": self.next_action, + "purpose_code": self.purpose_code, + "qualification_rule_artifact_digest": qualification_rule_artifact_digest, + "qualification_rule_artifact_reference": qualification_rule_artifact_reference, + "reason_code": reason_code, + "recorded_at": _canonical_timestamp(recorded_at), + "requester_actor_reference": requester_actor_reference, + "review_state": self.review_state, + "reviewed_at": _canonical_timestamp(reviewed_at), + "reviewer_actor_reference": reviewer_actor_reference, + "rule_category": rule_category, + "source_evidence_digest": source_evidence_digest, + "task_linkage_digest": task_linkage_digest, + "tenant_record_id": tenant_record_id, + } + + def _verified_payload(self) -> dict[str, object]: + """Return one verified snapshot or fail closed after any post-issuance mutation.""" + payload = self._validated_payload() + digest = sha256(_canonical_json(payload).encode("utf-8")).hexdigest() + with self._issuance_lock: + issued_digest = self._issuance_digests.get(self) + if issued_digest != digest: + raise ValueError("Job qualification-rule evidence was modified after issuance") + return payload + + def canonical_document(self) -> dict[str, object]: + """Return a detached verified canonical evidence document for audit persistence.""" + return dict(self._verified_payload()) + + def canonical_json(self) -> str: + """Return deterministic JSON from the exact verified evidence snapshot.""" + return _canonical_json(self._verified_payload()) + + def sha256_digest(self) -> str: + """Return SHA-256 over the exact verified canonical JSON evidence.""" + return sha256(self.canonical_json().encode("utf-8")).hexdigest() + + +def build_job_qualification_rule_review_packet( + *, + tenant_record_id: str, + job_record_reference: str, + job_analysis_snapshot_reference: str, + job_analysis_snapshot_digest: str, + qualification_rule_artifact_reference: str, + qualification_rule_artifact_digest: str, + task_linkage_digest: str, + ksao_linkage_digest: str, + source_evidence_digest: str, + rule_category: str, + effective_on: date, + requester_actor_reference: str, + reviewer_actor_reference: str, + reason_code: str, + evidence_version: int, + reviewed_at: datetime, +) -> JobQualificationRuleReviewPacket: + """Build one human-reviewed Job qualification-rule proposal without decision authority.""" + return JobQualificationRuleReviewPacket( + tenant_record_id=tenant_record_id, + job_record_reference=job_record_reference, + job_analysis_snapshot_reference=job_analysis_snapshot_reference, + job_analysis_snapshot_digest=job_analysis_snapshot_digest, + qualification_rule_artifact_reference=qualification_rule_artifact_reference, + qualification_rule_artifact_digest=qualification_rule_artifact_digest, + task_linkage_digest=task_linkage_digest, + ksao_linkage_digest=ksao_linkage_digest, + source_evidence_digest=source_evidence_digest, + rule_category=rule_category, + effective_on=effective_on, + requester_actor_reference=requester_actor_reference, + reviewer_actor_reference=reviewer_actor_reference, + reason_code=reason_code, + evidence_version=evidence_version, + reviewed_at=reviewed_at, + ) From 4c42b422fbca9d4bc79c04b67f778632d0be2aca Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:39:00 -0700 Subject: [PATCH 11/15] docs(job-analysis): use current Uniform Guidelines source --- docs/doctoring/job-qualification-rule-review-references.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/doctoring/job-qualification-rule-review-references.md b/docs/doctoring/job-qualification-rule-review-references.md index e7f9a40ce..804c6d6f8 100644 --- a/docs/doctoring/job-qualification-rule-review-references.md +++ b/docs/doctoring/job-qualification-rule-review-references.md @@ -14,8 +14,9 @@ U.S. Office of Personnel Management. (n.d.). *General Schedule qualification pol - OPM distinguishes job-analysis evidence from the ways applicants may demonstrate KSAs, including qualifying experience, education, or training. - Orgmetra uses that distinction only to keep Job qualification-rule provenance separate from candidate evaluation; it does not adopt federal qualification standards as a universal employer policy. -Equal Employment Opportunity Commission, Department of Justice, Department of Labor, & Civil Service Commission. (1978). *Uniform Guidelines on Employee Selection Procedures*, 29 C.F.R. pt. 1607. https://www.govinfo.gov/content/pkg/CFR-2023-title29-vol4/pdf/CFR-2023-title29-vol4-part1607.pdf +Equal Employment Opportunity Commission, Department of Justice, Department of Labor, & Civil Service Commission. (1978). *Uniform Guidelines on Employee Selection Procedures*, 29 C.F.R. pt. 1607. https://www.ecfr.gov/current/title-29/subtitle-B/chapter-XIV/part-1607 +- The eCFR current-title URL is the authoritative continuously updated regulatory publication target. Automated access was rate-limited during the 2026-08-24 review, so the regulation text was cross-checked against the official GovInfo annual CFR publication at https://www.govinfo.gov/content/pkg/CFR-2023-title29-vol4/pdf/CFR-2023-title29-vol4-part1607.pdf rather than treating the older annual edition as current law. - The Guidelines provide a federal framework for proper use, validity evidence, and documentation of employee selection procedures. - This package records review provenance only. It makes no finding that a qualification rule is legally required, job-related, validated, nondiscriminatory, or appropriate for a particular jurisdiction or applicant. From d92ac4cb798b3bd32b632c0ab677c03f944070e4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 15:43:50 -0700 Subject: [PATCH 12/15] test(job-analysis): cover malformed namespaced rule references --- packages/job-qualification-rule-review/tests/test_review.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/job-qualification-rule-review/tests/test_review.py b/packages/job-qualification-rule-review/tests/test_review.py index f77793342..fa55a29bc 100644 --- a/packages/job-qualification-rule-review/tests/test_review.py +++ b/packages/job-qualification-rule-review/tests/test_review.py @@ -149,6 +149,7 @@ def test_rejects_requester_reviewer_overlap() -> None: ("tenant_record_id", "ffffffff-ffff-ffff-ffff-ffffffffffff"), ("tenant_record_id", "not-a-uuid"), ("job_record_reference", f"position_record:{JOB_ID}"), + ("job_record_reference", "job_record:not-a-uuid"), ("job_record_reference", "job_record:00000000-0000-0000-0000-000000000000"), ("job_analysis_snapshot_reference", f"job_analysis:{SNAPSHOT_ID}"), ("qualification_rule_artifact_reference", f"qualification_rule:{RULE_ARTIFACT_ID}"), From 79adb79991921c15f637a6fd309bd86f8deb8de3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 10:50:20 +0900 Subject: [PATCH 13/15] fix(job-qualification-rule-review): make trust-boundary validation non-subclassable Strix OpenAI-direct scan of head d92ac4cb surfaced one MEDIUM finding: a hostile in-process subclass could override _validated_payload to bypass all trust-bearing field validation. Repair: __init_subclass__ now raises TypeError for every subclass, so the trust boundary is non-overridable by construction. Regression coverage asserts subclass definition fails closed at class-definition time and base-class canonical evidence stays stable per issued instance. 55 tests, 100% owned statement/branch coverage. --- .../review.py | 13 ++++++++++- .../tests/test_review.py | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py b/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py index 0caa7fcc8..6351914b4 100644 --- a/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py +++ b/packages/job-qualification-rule-review/src/orgmetra_job_qualification_rule_review/review.py @@ -155,7 +155,11 @@ def _canonical_json(payload: dict[str, object]) -> str: @dataclass(frozen=True, slots=True, weakref_slot=True, eq=False, repr=False) class JobQualificationRuleReviewPacket: - """Human-reviewed Job qualification-rule proposal without candidate-decision authority.""" + """Human-reviewed Job qualification-rule proposal without candidate-decision authority. + + The packet is deliberately non-subclassable: trust-boundary validation must + never be bypassable by an in-process subclass overriding the validation hook. + """ tenant_record_id: str job_record_reference: str @@ -185,6 +189,13 @@ class JobQualificationRuleReviewPacket: ] = WeakKeyDictionary() _issuance_lock: ClassVar[RLock] = RLock() + def __init_subclass__(cls, **kwargs: object) -> None: + """Reject every subclass so validation can never be overridden away.""" + raise TypeError( + "JobQualificationRuleReviewPacket must not be subclassed; " + "trust-boundary validation is non-overridable" + ) + def __post_init__(self) -> None: """Generate system time, validate all trust-bearing fields, and seal evidence.""" object.__setattr__(self, "recorded_at", datetime.now(timezone.utc)) diff --git a/packages/job-qualification-rule-review/tests/test_review.py b/packages/job-qualification-rule-review/tests/test_review.py index fa55a29bc..774c34ca4 100644 --- a/packages/job-qualification-rule-review/tests/test_review.py +++ b/packages/job-qualification-rule-review/tests/test_review.py @@ -353,3 +353,26 @@ def test_replace_remains_non_authoritative_and_reissues_system_time() -> None: assert second.decision_authority == "not_authorized_for_candidate_or_employment_decision" assert second.recorded_at >= first.recorded_at assert second.sha256_digest() != first.sha256_digest() + + +def test_subclass_cannot_override_the_trust_boundary() -> None: + """Subclass forgery must fail closed at class definition, before any instance.""" + + with pytest.raises(TypeError, match="must not be subclassed"): + + class ForgedPacket(JobQualificationRuleReviewPacket): + """A hostile subclass attempting to skip trust-boundary validation.""" + + def _validated_payload(self) -> dict[str, object]: + return {} + + +def test_valid_packets_still_canonicalize_and_hash_deterministically() -> None: + """Base-class construction keeps stable canonical evidence per issued instance.""" + first = build_job_qualification_rule_review_packet(**values()) + second = build_job_qualification_rule_review_packet(**values()) + + assert first.canonical_json() == first.canonical_json() + assert first.sha256_digest() == first.sha256_digest() + assert second.sha256_digest() != first.sha256_digest() + assert len(first.canonical_json()) > 0 From 14eab4eb21924f85c9d4eeef325ca37bfff37de3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 06:18:49 -0700 Subject: [PATCH 14/15] test(job-analysis): make qualification hardening regressions deterministic --- .../tests/test_review.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/job-qualification-rule-review/tests/test_review.py b/packages/job-qualification-rule-review/tests/test_review.py index 774c34ca4..bbc189e1b 100644 --- a/packages/job-qualification-rule-review/tests/test_review.py +++ b/packages/job-qualification-rule-review/tests/test_review.py @@ -358,19 +358,23 @@ def test_replace_remains_non_authoritative_and_reissues_system_time() -> None: def test_subclass_cannot_override_the_trust_boundary() -> None: """Subclass forgery must fail closed at class definition, before any instance.""" - with pytest.raises(TypeError, match="must not be subclassed"): - - class ForgedPacket(JobQualificationRuleReviewPacket): - """A hostile subclass attempting to skip trust-boundary validation.""" + def bypass_validation(_self: object) -> dict[str, object]: + return {} - def _validated_payload(self) -> dict[str, object]: - return {} + with pytest.raises(TypeError, match="must not be subclassed"): + type( + "ForgedPacket", + (JobQualificationRuleReviewPacket,), + {"_validated_payload": bypass_validation}, + ) def test_valid_packets_still_canonicalize_and_hash_deterministically() -> None: """Base-class construction keeps stable canonical evidence per issued instance.""" first = build_job_qualification_rule_review_packet(**values()) - second = build_job_qualification_rule_review_packet(**values()) + second_values = values() + second_values["rule_category"] = "experience_requirement" + second = build_job_qualification_rule_review_packet(**second_values) assert first.canonical_json() == first.canonical_json() assert first.sha256_digest() == first.sha256_digest() From fde420ae11680a8b54eada785683db1afecca6bd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 16:00:41 +0900 Subject: [PATCH 15/15] docs(job-analysis): align qualification review traceability with protected truth --- docs/traceability/job-qualification-rule-review.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/traceability/job-qualification-rule-review.md b/docs/traceability/job-qualification-rule-review.md index fee6265de..a88505804 100644 --- a/docs/traceability/job-qualification-rule-review.md +++ b/docs/traceability/job-qualification-rule-review.md @@ -2,8 +2,8 @@ ## State boundary -- **Protected-main truth:** `develop@9e3e4847510e1e612b48474ba42b177b8ed824df` has authoritative Job Analysis Task/FJA/KSAO evidence but no dedicated governed qualification-rule review packet. -- **Active PR truth:** this branch proposes `JobQualificationRuleReviewPacket`; it is not protected-main truth until merged. +- **Protected-parent adoption snapshot:** `develop@eb9757f8649aaad026a9865508d9aad50c1a7a4f` includes protected #161's repository-quality consolidation and remains the parent adopted by this Draft lane. The protected branch must be re-read again before merge; this snapshot is evidence of the adoption point, not a perpetual claim about the latest protected head. +- **Active PR truth:** this Draft branch proposes `JobQualificationRuleReviewPacket`; it is not protected-main truth until normal protected integration completes. - **Foreign dependencies:** none are written or queried directly. This slice is Orgmetra-owned and standalone. ## Requirement-to-evidence matrix @@ -19,7 +19,7 @@ | Require accountable human review | distinct requester/reviewer UUIDv4 correlations and mandatory review state | actor-overlap and governance regressions | | Version high-impact evidence | bounded exact integer `evidence_version` | bool/non-int/range and digest-change regressions | | Detect in-process post-issuance mutation | process-local weak issuance digest outside packet slots; export revalidates one snapshot | valid-field and hostile-runtime mutation regressions | -| Preserve exact package/artifact evidence | dedicated exact-head workflow builds and SHA-256-binds isolated wheel | Job Qualification Rule Review Quality | +| Preserve exact package/artifact evidence | canonical Foundation CI builds and SHA-256-binds the isolated wheel after leaf-workflow retirement | Foundation installed-artifact contract + leaf-reintroduction regression | | Preserve exact owned test sufficiency | package pytest config requires statement and branch coverage 100% | `--cov-fail-under=100`, branch coverage enabled | ## Authoritative next action