Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
e033813
test: define candidate evidence intake contract
seonghobae Aug 18, 2026
1dc2f13
feat: add governed candidate evidence intake
seonghobae Aug 18, 2026
84b5554
test: require actionable evidence sealing handoff
seonghobae Aug 18, 2026
e8563f7
fix: make candidate evidence next action explicit
seonghobae Aug 18, 2026
e6a640e
docs: correct candidate evidence source metadata
seonghobae Aug 18, 2026
ece860c
fix: keep candidate evidence changelog unreleased
seonghobae Aug 18, 2026
6822ca9
test: harden candidate evidence references
seonghobae Aug 19, 2026
71370f4
fix: enforce opaque candidate evidence references
seonghobae Aug 19, 2026
2a61de8
docs: harden candidate evidence references
seonghobae Aug 19, 2026
6fb4cb5
docs: record opaque candidate evidence identity
seonghobae Aug 19, 2026
16c1680
docs: trace candidate reference hardening
seonghobae Aug 19, 2026
6336c29
docs: note candidate reference hardening
seonghobae Aug 19, 2026
57f2452
test: require complete candidate evidence docstrings
seonghobae Aug 19, 2026
bd443ca
fix: make candidate evidence tests beginner-readable
seonghobae Aug 19, 2026
64442f4
merge: reconcile candidate evidence with current protected develop
seonghobae Aug 19, 2026
d4fb006
merge: reconcile candidate evidence with latest protected develop
seonghobae Aug 19, 2026
5af8af4
chore: reconcile candidate evidence with protected develop
seonghobae Aug 19, 2026
2a22cb4
test: close candidate evidence reason vocabulary
seonghobae Aug 19, 2026
d166535
fix: close candidate evidence reason vocabulary
seonghobae Aug 19, 2026
c9a0d31
Merge branch 'develop' into feat/candidate-evidence-intake
github-actions[bot] Aug 19, 2026
81800af
test: require candidate evidence versioning
seonghobae Aug 19, 2026
2c97101
fix: version candidate evidence packets
seonghobae Aug 19, 2026
790ab9f
docs: bind candidate evidence version
seonghobae Aug 19, 2026
dabcebf
docs: trace candidate evidence versioning
seonghobae Aug 19, 2026
b26df5c
chore: reconcile candidate evidence intake onto develop
seonghobae Aug 19, 2026
a74ab47
test(candidate-evidence): require tenant-scoped reference resolution
seonghobae Aug 19, 2026
1434087
fix(candidate-evidence): bind intake references to tenant scope
seonghobae Aug 19, 2026
d8d81ca
test(candidate-evidence): align governed next action
seonghobae Aug 19, 2026
18c7f0c
docs(candidate-evidence): align tenant evidence boundary
seonghobae Aug 19, 2026
6578063
docs(candidate-evidence): require exact-tenant resolution
seonghobae Aug 19, 2026
6117902
docs(candidate-evidence): trace tenant-resolution regression
seonghobae Aug 19, 2026
4096d24
docs(candidate-evidence): record tenant binding repair
seonghobae Aug 19, 2026
f63ebb9
merge: reconcile candidate evidence with protected develop
seonghobae Aug 19, 2026
61b416a
test(candidate-evidence): align tenant next-action assertion
seonghobae Aug 19, 2026
79ea34b
chore(candidate-evidence): reconcile protected develop
seonghobae Aug 19, 2026
0bc62b2
test: reject UUIDv1 candidate evidence trust references
seonghobae Aug 20, 2026
c483f91
fix: require UUIDv4 candidate evidence trust references
seonghobae Aug 20, 2026
0c60c78
docs: define UUIDv4 candidate evidence references
seonghobae Aug 20, 2026
5c959c9
docs: record UUIDv4 candidate evidence decision
seonghobae Aug 20, 2026
2f6e526
docs: record candidate UUIDv4 privacy hardening
seonghobae Aug 20, 2026
66af0d8
docs: trace UUIDv4 candidate evidence regression
seonghobae Aug 20, 2026
a806204
test: reject correlating tenant UUIDv1 in candidate intake
seonghobae Aug 20, 2026
379eeeb
fix: require opaque UUIDv4 tenant identity in candidate intake
seonghobae Aug 20, 2026
71f7fa8
docs: bind candidate intake tenant identity to UUIDv4 opacity
seonghobae Aug 20, 2026
dc162ce
docs: require opaque UUIDv4 tenant identity for candidate intake
seonghobae Aug 20, 2026
57edb18
docs: trace opaque UUIDv4 tenant identity in candidate intake
seonghobae Aug 20, 2026
b64c411
docs: record UUIDv4 tenant privacy hardening
seonghobae Aug 20, 2026
b0889f5
fix: preserve candidate packet positional compatibility
seonghobae Aug 20, 2026
c793427
Merge remote-tracking branch 'refs/remotes/origin/develop' into HEAD
seonghobae Aug 20, 2026
c4154eb
test: require candidate evidence to accept core tenant UUIDv7
seonghobae Aug 20, 2026
d746da2
fix: honor authoritative tenant UUID contract in candidate evidence
seonghobae Aug 20, 2026
a0c223b
docs: align candidate evidence tenant identity with core
seonghobae Aug 20, 2026
0ca1c1a
docs: separate candidate packet and tenant UUID ownership
seonghobae Aug 20, 2026
4465590
docs: trace candidate tenant UUID interoperability
seonghobae Aug 20, 2026
8b131d7
docs: record candidate tenant identity interoperability repair
seonghobae Aug 20, 2026
ece5c1c
chore: integrate protected develop into candidate-evidence lane
seonghobae Aug 20, 2026
a009cba
chore: reconcile candidate evidence intake with current develop
seonghobae Aug 20, 2026
03405df
fix(candidate-evidence): reserve unique ADR number
seonghobae Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/candidate-evidence-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Candidate Evidence Quality

on:
pull_request:
branches:
- develop
paths:
- "packages/candidate-evidence/**"
- ".github/requirements/foundation-test.txt"
- ".github/workflows/candidate-evidence-quality.yml"
- "docs/adr/0025-governed-candidate-evidence-intake.md"
- "docs/doctoring/candidate-evidence-intake-references.md"
- "docs/traceability/candidate-evidence-intake.md"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: candidate-evidence-quality-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
unit:
name: Candidate evidence 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 candidate evidence package
run: python -m compileall -q packages/candidate-evidence/src packages/candidate-evidence/tests
- name: Test candidate evidence with exact statement and branch coverage
env:
PYTHONPATH: packages/candidate-evidence/src
COVERAGE_FILE: /tmp/orgmetra-candidate-evidence.coverage
run: python -m pytest -c packages/candidate-evidence/pyproject.toml packages/candidate-evidence/tests
- name: Require clean checkout
run: |
git diff --exit-code
test -z "$(git status --porcelain)"
47 changes: 47 additions & 0 deletions docs/adr/0025-governed-candidate-evidence-intake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# ADR 0025: Govern candidate evidence intake as reference-only evidence

- **Status:** Proposed — active PR only
- **Date:** 2026-08-19
Comment thread
seonghobae marked this conversation as resolved.

## Context

Orgmetra already owns candidate profiles, sealed selection-decision evidence, candidate-to-worker conversion, governed requisition review, and candidate-neutral interview planning. A buyer still needs a defensible intake boundary between receiving candidate-related material and later sealing or using it in a high-impact selection decision.

Copying resumes, assessment values, demographic attributes, or other candidate content into every workflow envelope increases privacy exposure and makes retention, provenance, and purpose control harder to audit. A nominally opaque packet-owned reference is also unsafe if arbitrary human-readable/value-bearing suffixes are accepted: candidate names or other sensitive values can then be smuggled through reference fields. UUIDv1 adds a subtler correlation channel for packet-owned references because timestamp/node-derived metadata can be embedded in an otherwise canonical UUID. 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. Ordinary object representations can create the same leak if they print sensitive correlation metadata in logs or assertion failures. Canonical UUID syntax also does not prove tenant ownership, so a valid reference from another tenant must not be accepted as authoritative evidence merely because its shape is correct.

## Decision

Add a transport-neutral `CandidateEvidenceIntakePacket` that binds:

- canonical non-sentinel Orgmetra tenant identity and one UUIDv4-backed opaque intake reference;
- UUIDv4-backed candidate-profile, requisition, authoritative Job, job-requirements, evidence-set, provenance, handling-policy, retention-policy and actor references;
- independent SHA-256 digests where content drift matters;
- one accountable actor, bounded evidence-item count, fixed `candidate_evidence_intake` purpose, bounded reason metadata, and precision-preserving UTC collection time;
- mandatory human confirmation, immutable `requires_human_review` state, actionable next-step copy, and a fully redacted ordinary object representation.

`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 references separately require canonical non-sentinel UUIDv4 and the expected prefix. Human-readable/value-bearing suffixes, UUIDv1, and other non-v4 suffixes are rejected fail-closed for those references. Before sealing or accountable review, the host must re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary, then prove candidate↔requisition↔Job correlation and verify provenance, handling, retention, and completeness. UUID syntax is only an early shape/opacity guard; it is not tenant authority or relationship evidence. The governance packet contains no candidate name, email, demographic attribute, assessment value, raw document content, credential, or free-form model output. UUID-backed tenant and candidate correlation remain sensitive metadata rather than anonymous data.

Canonical JSON plus SHA-256 provide immutable audit correlation but do not establish evidence truth, scientific validity, fairness, legal compliance, authorization, policy enforcement, or final approval.

## Consequences

### Positive

- Recruiting workflows can correlate candidate evidence to the exact Job/requisition and policy versions without duplicating candidate content.
- Packet-owned reference fields cannot quietly become a human-readable PII/value channel or UUIDv1 timestamp/node correlation channel, while the leaf package remains interoperable with authoritative Orgmetra tenant UUIDs.
- Cross-tenant evidence mixing is fail-closed at the host sealing/review boundary because every packet reference must resolve within the packet tenant.
- Source provenance, purpose-bound handling, and retention are explicit versioned evidence rather than prose-only assumptions.
- Ordinary logging/assertion formatting does not emit candidate correlation or evidence digests.
- The package remains standalone and MSA-friendly and performs no cross-service application-table SQL.

### Costs and constraints

- Packet-owned trust references must use durable UUIDv4-backed public identifiers and must be authoritatively resolved in the exact tenant before use. Tenant UUID generation/privacy policy remains owned by the authoritative HRIS boundary.
- The packet does not store raw candidate evidence, decide whether an item is lawfully usable, or prove the referenced policy was followed.
- UUID-backed tenant and candidate references are still sensitive correlating metadata and require least-privilege handling.
- Evidence sealing, authoritative selection decisions, immutable audit/outbox, deletion/retention execution, export controls, accommodations, adverse-impact monitoring, and jurisdiction-specific legal review remain separate obligations.
- This ADR remains proposed until its exact PR head merges into protected `develop`.

## References

See `docs/doctoring/candidate-evidence-intake-references.md`.
17 changes: 17 additions & 0 deletions docs/doctoring/candidate-evidence-intake-references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Candidate evidence intake references

These sources inform the active-PR intake boundary. They do not establish certification, jurisdiction-specific legal compliance, or permission to collect/use any particular candidate evidence item.

## APA 7 references

International Organization for Standardization. (2023). *ISO 30405:2023 human resource management — Guidelines on recruitment* (2nd ed.). https://www.iso.org/standard/79488.html

International Organization for Standardization, & International Electrotechnical Commission. (2025). *ISO/IEC 27701:2025 information security, cybersecurity and privacy protection — Privacy information management systems — Requirements and guidance* (2nd ed.). https://www.iso.org/standard/27701

U.S. Equal Employment Opportunity Commission. (n.d.). *Regulations and guidelines*. Retrieved August 19, 2026, from https://www.eeoc.gov/regulations-and-guidelines

U.S. Equal Employment Opportunity Commission. (2007, December 1). *Employment tests and selection procedures*. https://www.eeoc.gov/laws/guidance/employment-tests-and-selection-procedures

## Applied boundary

ISO 30405:2023 provides current recruitment-process guidance spanning preparation, sourcing, assessment, employment and review. ISO/IEC 27701:2025 provides current requirements and guidance for privacy-information management. EEOC identifies 29 CFR Part 1607 as the Uniform Guidelines on Employee Selection Procedures and notes that selection procedures should be evaluated for job relatedness and disparate impact. Orgmetra therefore keeps candidate values outside this governance packet while binding the exact Job requirements, evidence/provenance identities, handling and retention policy versions, accountable actor, purpose and human-review state needed for downstream audit.
23 changes: 23 additions & 0 deletions docs/traceability/candidate-evidence-intake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Candidate evidence intake traceability

- **Maturity:** `implemented_on_active_pr`
- **Buyer capability:** Candidate Evidence workspace governance boundary
- **Owned contract:** `CandidateEvidenceIntakePacket`

| Requirement | Evidence |
|---|---|
| Correlate candidate evidence to the correct recruiting context | Canonical non-sentinel tenant identity under the Orgmetra core operational-UUID contract plus UUIDv4-backed candidate-profile, requisition and Job references; immutable next action requires every packet reference to be re-resolved within the exact tenant and candidate↔requisition↔Job correlation verified before sealing/review |
| Prevent cross-tenant evidence mixing | `test_tenant_scope.py` requires tenant-scoped resolution of every packet reference before correlation/provenance checks and authoritative sealing; UUID syntax alone is not tenant authority |
| Prevent packet-owned reference fields from becoming a covert PII/value/correlation channel 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. Human-readable/value-bearing suffixes and UUIDv1/non-v4 reference suffixes are rejected; `test_reference_privacy.py` covers authoritative UUIDv7 tenant interoperability plus every trust-reference field through builder and replacement paths |
| Preserve job-related evidence context | Versioned job-requirements reference + SHA-256 digest |
| Preserve evidence identity and source provenance without copying values | Evidence-set and source-provenance references + independent SHA-256 digests |
| Bind privacy/operational handling | Handling-policy and retention-policy references + independent SHA-256 digests |
| 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 |
| Preserve accountable collection context | Actor reference, fixed purpose, bounded reason, exact evidence-item count and precision-preserving UTC time |
| Prevent packet-as-decision misuse | Exact boolean human confirmation, immutable `requires_human_review` state and fixed next action |
| Prevent ordinary logs/assertions from leaking candidate correlation | generated dataclass repr disabled; `repr(packet)` is `CandidateEvidenceIntakePacket(<redacted>)`; repr-redaction regression |
| Preserve deterministic audit correlation | Canonical JSON + independently testable SHA-256 digest |
| Minimize candidate content exposure | No candidate name/email/demographic attribute/assessment value/raw evidence/credential/free-form model output in the packet |
| Keep service ownership boundaries intact | No database migration, no provider execution, no cross-service application-table SQL |

The packet is correlation evidence, not proof of evidence truth, tenant ownership until authoritative resolution, lawful use, selection validity, fairness, policy execution, evidence sealing, or a final employment decision. UUIDv4 is an opacity/privacy constraint for packet-owned trust references only; tenant UUID generation/privacy policy remains owned by the authoritative HRIS boundary. Those claims require their own owner-bound evidence.
11 changes: 11 additions & 0 deletions packages/candidate-evidence/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

## Unreleased

- Add a PII-minimized candidate-evidence intake packet with exact candidate/requisition/Job/job-requirements correlation.
- Follow Orgmetra's authoritative canonical non-sentinel operational UUID contract for `tenant_record_id`, while packet-owned evidence, source-provenance, handling and retention references remain canonical UUIDv4-backed opaque identity; content-bearing evidence also carries SHA-256 digests.
- Reject human-readable/value-bearing reference suffixes, sentinel/noncanonical reference UUIDs, and UUIDv1/non-v4 packet-reference suffixes so trust-reference fields cannot become a covert candidate-data or correlation channel without duplicating tenant identity policy.
- Require every packet reference to be re-resolved within the exact tenant through its authoritative boundary before candidate↔requisition↔Job correlation, evidence sealing, or accountable review, preventing cross-tenant evidence mixing behind valid UUID syntax.
- Redact the ordinary packet representation so candidate correlation and evidence digests are not emitted through routine logs/assertion failures.
- Require bounded evidence counts, fixed purpose, explicit human-review state, precision-preserving UTC timestamps, deterministic canonical JSON, and exact packet SHA-256.
- Keep raw candidate evidence, demographic attributes, assessment values, credentials, and free-form model output outside the governance packet.
11 changes: 11 additions & 0 deletions packages/candidate-evidence/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Orgmetra candidate evidence intake

This package defines a transport-neutral, reference-only packet for admitting candidate evidence into an accountable recruiting workflow without copying the candidate's evidence values into the governance envelope.

`CandidateEvidenceIntakePacket` binds one tenant, candidate profile, requisition, authoritative Job, versioned job requirements, evidence set, source-provenance manifest, handling policy, retention policy, accountable actor, purpose/reason, bounded positive `evidence_version`, evidence-item count, and collection time. `tenant_record_id` follows Orgmetra's authoritative canonical non-sentinel operational UUID contract rather than imposing a second UUID-version policy at this leaf package. Packet-owned trust-bearing references remain canonical non-sentinel UUIDv4 values and additionally require their expected namespaces. 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. Content-bearing evidence also carries an independent lowercase SHA-256 digest. `evidence_version` is part of canonical JSON and SHA-256 evidence so actor/purpose/reason evidence from different contract revisions cannot silently collide; it must be a true integer from 1 through 2147483647.

The packet deliberately contains no candidate name, email, demographic attribute, assessment value, raw resume/document content, credential, or free-form model output. Human-readable/value-bearing reference suffixes are rejected so those fields cannot become a covert PII channel. UUID-backed tenant and candidate correlation remain sensitive metadata and must still be handled under the bound purpose, handling policy, retention policy, least-privilege authorization, export controls, and audit boundary. The ordinary object representation is fully redacted to avoid leaking correlation/evidence through logs or assertion failures.

The packet cannot represent approval. `human_confirmation_required` must be the boolean singleton `True`, `review_state` is fixed to `requires_human_review`, and its immutable next action requires the host to re-resolve **every packet reference** within the exact `tenant_record_id` through its authoritative boundary before evidence sealing. The host must then verify candidate↔requisition↔Job correlation, job relevance, source provenance, permitted handling, retention, and evidence completeness before requesting authoritative evidence sealing and accountable human review. Reference UUIDv4 syntax constrains packet-owned trust-reference opacity only; it does not prove tenant ownership, record relationship, or authorization, and tenant UUID generation/privacy policy remains owned by the authoritative HRIS boundary.

Canonical JSON and SHA-256 support immutable correlation only. They prove neither the truth of referenced evidence nor selection validity, fairness, legal compliance, authorization to use a particular evidence item, or final human approval. Authoritative persistence, tenant-scoped reference resolution, evidence sealing, selection decisions, audit/outbox, and candidate-to-worker conversion remain separate Orgmetra boundaries.
24 changes: 24 additions & 0 deletions packages/candidate-evidence/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[build-system]
requires = ["setuptools>=69"]
build-backend = "setuptools.build_meta"

[project]
name = "orgmetra-candidate-evidence"
version = "0.1.0"
description = "PII-minimized candidate-evidence intake correlation 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_candidate_evidence",
"--cov-branch",
"--cov-report=term-missing",
"--cov-fail-under=100",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Public candidate-evidence intake contract."""

from .packet import CandidateEvidenceIntakePacket, build_candidate_evidence_intake_packet

__all__ = ["CandidateEvidenceIntakePacket", "build_candidate_evidence_intake_packet"]
Loading
Loading