Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
de20a17
test(position): define lifecycle review package contract
seonghobae Aug 24, 2026
b5d0284
test(position): add lifecycle review RED regressions
seonghobae Aug 24, 2026
9578423
test(position): add lifecycle review exact-head quality gate
seonghobae Aug 24, 2026
2991342
test(position): isolate lifecycle review correlations
seonghobae Aug 24, 2026
f54de33
feat(position): implement governed lifecycle review evidence
seonghobae Aug 24, 2026
cb4bfa0
feat(position): expose lifecycle review contract
seonghobae Aug 24, 2026
9d21850
test(position): cover lifecycle validation edges
seonghobae Aug 24, 2026
31ec54c
docs(position): explain lifecycle review boundary
seonghobae Aug 24, 2026
4ce018b
docs(position): record lifecycle review contract
seonghobae Aug 24, 2026
2a0c88c
docs(position): record lifecycle review ADR
seonghobae Aug 24, 2026
4f4eea5
docs(position): trace lifecycle review evidence
seonghobae Aug 24, 2026
b9e85a1
docs(position): add lifecycle review primary references
seonghobae Aug 24, 2026
0fe08c8
test(position): require truthful branch-protection docs
seonghobae Aug 24, 2026
794dc50
docs(position): distinguish product truth from branch protection
seonghobae Aug 24, 2026
bfedd3e
docs(position): record protection restoration prerequisite
seonghobae Aug 24, 2026
cfff42f
docs(position): make lifecycle README protection-truthful
seonghobae Aug 24, 2026
ef3f453
test(position-lifecycle): reject obsolete protection-absent claims
seonghobae Aug 25, 2026
6fb064e
docs(position-lifecycle): align README with effective ruleset
seonghobae Aug 25, 2026
8545b4b
docs(position-lifecycle): align ADR with effective ruleset
seonghobae Aug 25, 2026
631002e
docs(position-lifecycle): align traceability with effective ruleset
seonghobae Aug 25, 2026
ca19d18
test(position-lifecycle): accept semantic issue reference casing
seonghobae Aug 25, 2026
de9fc67
fix(position-lifecycle): make binding-drift release path coverable
seonghobae Aug 25, 2026
03f3f6d
test(position-lifecycle): drop unused local in drift regression
seonghobae Aug 25, 2026
25aaef4
ci(position): reconcile lifecycle review with Foundation
seonghobae Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/adr/0111-governed-position-lifecycle-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ADR 0111: Govern Position lifecycle-change review separately from mutation

Status: Proposed

## Context

Current `develop@eb9757f8649aaad026a9865508d9aad50c1a7a4f` product truth stores Job, Position, and Assignment separately and already recognizes Position lifecycle vocabulary, but the shipped People mutation path only creates Position records. Repository governance is a separate concern: effective organization ruleset `18156473` currently requires one approving review, stale-review dismissal after push, review-thread resolution, extra approval for unattributed changes, seven central required workflows, and non-fast-forward/deletion protection; `require_last_push_approval` is false. Issue #89 owns the remaining governance/control-plane gap, including routine `OrganizationAdmin/always` bypass. None of those repository controls is implied by this ADR, and their live payload must be re-read before integration/release claims. Vacancy evidence and vacancy-to-assignment orchestration do not own a review contract for freezing, closing, abolishing, or reactivating an existing seat.

A lifecycle change can affect staffing availability and later workforce evidence. Reusing a generic Position-creation command or a reporting-line review would blur evidence ownership and could let cached/UI state substitute for current bitemporal truth.

## Decision

Add an Orgmetra-owned `PositionLifecycleChangeReviewPacket` as a transport-neutral, value-minimized human-review artifact.

The packet binds one tenant-qualified Position, current/proposed lifecycle state, business-effective date, reviewed Position/Assignment snapshot digests, pseudonymous requester/reviewer separation, controlled reason/outcome, evidence version, and human-review/system-recorded UTC chronology. `abolished` is terminal and no-op transitions are rejected.

The packet never authorizes mutation. An approved review remains `requires_authoritative_resolution` and `not_authorized_to_apply`. The later authoritative host must freshly resolve bitemporal Position and Assignment truth, re-establish actor authority/separation and staffing safety, validate the reviewed evidence, and commit the mutation with immutable audit/outbox.

Package-level quality remains subordinate to protected repository ownership: the retired Position Lifecycle Review leaf workflow stays deleted. Its useful exact CPython 3.14.7, installed-wheel provenance, and 100% statement/branch coverage obligations execute through canonical Foundation CI with an executable non-resurrection regression.

## Consequences

- Lifecycle review evidence cannot silently become Position truth.
- Existing Position/Assignment source-of-truth boundaries remain unchanged.
- No Person/candidate identity, compensation, assessment, rating, allocation value, credential, prompt, or model output is copied into the review evidence.
- A later bounded mutation/persistence slice remains necessary; this ADR does not claim it is shipped.
- Process-local issuance/reference binding is defense in depth, not distributed durability or authorization.
- Conflict-free Git protected-parent adoption is insufficient if it resurrects a workflow retired by the protected owner; semantic reconciliation must preserve the protected owner and the useful evidence contract separately.

## Alternatives rejected

1. **Encode lifecycle change as a new Position creation.** Rejected because it would conflate stable Position identity with versioned lifecycle state.
2. **Reuse vacancy or reporting-line review evidence.** Rejected because those artifacts prove different facts and do not own lifecycle semantics.
3. **Allow the review packet itself to mutate Position truth.** Rejected because current Position/Assignment truth and staffing safety must be re-resolved at the authoritative transaction boundary.
4. **Restore the package-local quality workflow during protected-parent adoption.** Rejected because protected #161 centralized repository-owned PR quality and pinned the canonical runner contract; the package's useful artifact/coverage obligation belongs under that owner rather than in a resurrected leaf.
17 changes: 17 additions & 0 deletions docs/doctoring/position-lifecycle-review-references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Position lifecycle review — primary references

Reviewed 2026-08-24 (UTC/KST calendar date as applicable). These references support identifier, privacy-risk, and workforce-evidence design only; Orgmetra does not claim certification or reproduce licensed ISO metric definitions.

## APA 7 references

Davis, K., Peabody, B., & Leach, P. (2024). *Universally Unique IDentifiers (UUIDs)* (RFC 9562). RFC Editor. https://doi.org/10.17487/RFC9562

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

National Institute of Standards and Technology. (2020). *NIST Privacy Framework: A tool for improving privacy through enterprise risk management, version 1.0* (NIST CSWP 10). https://doi.org/10.6028/NIST.CSWP.10

## Design use

- RFC 9562 is the primary identifier source for UUID layouts, including UUIDv4, UUIDv7, Nil, and Max. Orgmetra keeps authoritative HRIS operational UUID interoperability separate from packet-owned UUIDv4 correlation identifiers.
- ISO 30414:2025 is currently published as Edition 2 (2025-08) and includes workforce composition and mobility/succession among its human-capital reporting areas. It motivates historically defensible workforce/Position evidence, not a licensed metric implementation or conformity claim.
- NIST Privacy Framework 1.0 is a final, voluntary risk-management framework. It motivates value minimization and separating durable governance evidence from unnecessary Person/candidate/compensation payloads. NIST also lists Privacy Framework 1.1 as a newer project; this ADR does not present non-final work as the final baseline.
27 changes: 27 additions & 0 deletions docs/traceability/position-lifecycle-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Position lifecycle review traceability

## Truth state

- **Default-branch product truth:** `develop@eb9757f8649aaad026a9865508d9aad50c1a7a4f` keeps Job, Position, and Assignment as distinct HRIS facts, owns Position lifecycle vocabulary, and carries purpose-bound authorization plus immutable audit/outbox foundations. It does not yet integrate this active PR's dedicated existing-Position lifecycle review artifact or authoritative lifecycle mutation.
- **Repository governance truth:** effective organization ruleset `18156473` applies to the default branch. The live payload requires one approving review, stale-review dismissal after push, review-thread resolution, extra approval for unattributed changes, seven central required workflows, and deletion/non-fast-forward protection; `require_last_push_approval` is currently false. Issue #89 owns the remaining governance/control-plane gap, including routine `OrganizationAdmin/always` bypass. These repository controls are not product capability and must be re-read before merge/release claims.
- **Active PR truth:** this branch adds only the governed review evidence packet and an exact-head installed-artifact/coverage contract consolidated under canonical Foundation CI.
- **Planned:** authoritative bitemporal Position lifecycle mutation/persistence that consumes reviewed evidence, re-resolves staffing truth, and atomically records immutable audit/outbox.
- **Out of scope:** autonomous employment decisions, Person/candidate data, compensation, assessment/rating data, reporting-line mutation, Keyverse/Naruon/other CWL repository writes, and direct foreign application-table SQL.

## Requirement mapping

| Requirement | Evidence |
|---|---|
| Separate Position identity from lifecycle review evidence | `PositionLifecycleChangeReviewPacket`; ADR 0111 |
| Preserve business-effective and system-recorded time | `effective_on`, `reviewed_at`, `recorded_at`; chronology regressions |
| Human review with requester/reviewer separation | pseudonymous actor UUIDv4 correlations; separation regression |
| Purposefully non-authorizing high-impact evidence | fixed `human_reviewed`, `requires_authoritative_resolution`, `not_authorized_to_apply`, `human_review_only` states |
| Re-resolve staffing truth before mutation | reviewed Position/Assignment snapshot SHA-256 digests plus fixed approved next action |
| Minimize PII and high-impact payloads | packet carries no Person/candidate identity, allocation, compensation, rating, assessment, free text, credential, prompt, or model output |
| Fail closed on lifecycle ambiguity | explicit transition vocabulary; no-op rejection; abolished terminal; reason bound to target status |
| Tamper/correlation defense in depth | issuance digest verification; live tenant-qualified review-reference binding; adversarial `object.__setattr__` and `dataclasses.replace` regressions |
| Exact owned test coverage without retired leaf CI | canonical Foundation delegates to `tests/test_position_lifecycle_review_artifact.sh`, which requires CPython 3.14.7, hash-bound isolated wheel installation, and the package's exact `--cov-branch --cov-fail-under=100` contract; `test_artifact_execution.py` rejects leaf-workflow resurrection |

## Buyer behavior

A buyer-facing workflow can collect a review of a proposed Position freeze/closure/abolition/reactivation without claiming the seat has changed. If the review outcome is rejected, the only next action is not to apply it. If approved for authoritative resolution, the next action explicitly requires fresh tenant-qualified Position/Assignment truth, actor authority/separation, staffing safety, evidence validation, and immutable audit/outbox before any mutation.
8 changes: 8 additions & 0 deletions packages/position-lifecycle-review/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

## 0.1.0 - Unreleased

- Add `PositionLifecycleChangeReviewPacket` for value-minimized human review of existing Position status changes.
- Keep all reviewed evidence non-authorizing until fresh authoritative bitemporal Position/Assignment resolution and immutable audit/outbox mutation.
- Reject no-op transitions, resurrection of abolished Positions, ungoverned status/reason/outcome vocabulary, noncanonical actor/digest/time evidence, hostile runtime scalar subclasses, post-issuance mutation, and conflicting live change-reference reuse.
- Preserve the installed-wheel quality contract under canonical Foundation CI instead of restoring the retired package-local workflow: exact CPython 3.14.7, hash-bound isolated wheel installation, and 100% owned statement/branch coverage remain required.
45 changes: 45 additions & 0 deletions packages/position-lifecycle-review/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Orgmetra Position Lifecycle Review

This package records **human-reviewed evidence for a proposed lifecycle change to an existing Position**. It is deliberately not the authoritative Position mutation boundary.

## Why it exists

Current Orgmetra `develop@eb9757f8649aaad026a9865508d9aad50c1a7a4f` product truth already treats `Job`, `Position`, and `Assignment` as separate HRIS facts and recognizes Position statuses `open`, `active`, `frozen`, `closed`, and `abolished`. Repository control is separate from that product claim: effective organization ruleset `18156473` currently requires one approving review, stale-review dismissal after push, review-thread resolution, extra approval for unattributed changes, seven central required workflows, and non-fast-forward/deletion protection; last-push approval is not currently required. Repository-governance issue #89 remains open for the control-plane gap, including routine `OrganizationAdmin/always` bypass. Those controls must be re-read before merge/release claims and do not make this unmerged package protected product truth.

A commercial HRIS also needs review evidence before a seat is frozen, closed, abolished, or reactivated, because those changes can alter staffing availability and downstream workforce reporting.

`PositionLifecycleChangeReviewPacket` binds:

- tenant and Position operational UUIDs;
- one packet-owned UUIDv4 change correlation;
- current/proposed lifecycle status and business-effective date;
- exact reviewed Position and Assignment snapshot SHA-256 digests;
- pseudonymous requester/reviewer UUIDv4 actor correlations with separation;
- controlled reason and human review outcome;
- evidence schema version 1;
- human-review time and later-or-equal system-recorded UTC time.

It intentionally carries **no Person/candidate identity, name, email, compensation, assessment, rating, allocation value, credential, prompt, or model output**.

## Authority boundary

Even an `approved_for_authoritative_resolution` review remains:

- `human_reviewed`;
- `requires_authoritative_resolution`;
- `not_authorized_to_apply`;
- `human_review_only`.

Before a lifecycle mutation, the Orgmetra host must freshly re-resolve tenant-qualified bitemporal Position and Assignment truth at the requested business/system coordinate, re-resolve requester/reviewer authority, prove staffing safety, validate the reviewed evidence, and commit the actual Position mutation with immutable audit/outbox in the authoritative transaction. A rejected review must not be applied.

The packet permits reviewed transitions among normal seat states but treats `abolished` as terminal. No-op status changes are rejected. A reason token is tied to the proposed state so a valid token cannot be reused with different lifecycle semantics.

## Integrity boundary

Trust-bearing scalar values require exact built-in runtime types. Canonical evidence is deterministic JSON with SHA-256 content digest and a redacted routine representation. A process-local issuance seal detects post-construction field rewriting, while a live tenant-qualified change-reference binding prevents one correlation from denoting conflicting evidence while any duplicate packet remains alive. These are defense-in-depth controls only; durable uniqueness and mutation authorization belong to authoritative persistence/audit.

Package-quality evidence is owned by canonical Foundation CI rather than a package-local workflow. The active branch keeps the retired `position-lifecycle-review-quality.yml` absent and uses a repository-owned artifact contract to require CPython 3.14.7, a hash-bound isolated wheel installation, and exact 100% statement/branch coverage without package-local `PYTHONPATH` execution.

## Scope

This package writes only Orgmetra evidence. It does not modify Keyverse, Naruon, Contextual Orchestrator, or another dedicated-writer repository and does not query a foreign application table.
24 changes: 24 additions & 0 deletions packages/position-lifecycle-review/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[build-system]
requires = ["setuptools>=84"]
build-backend = "setuptools.build_meta"

[project]
name = "orgmetra-position-lifecycle-review"
version = "0.1.0"
description = "Governed Position lifecycle-change review evidence for Orgmetra."
requires-python = ">=3.14"

[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_position_lifecycle_review",
"--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 API for governed Position lifecycle-change review evidence."""

from .review import PositionLifecycleChangeReviewPacket

__all__ = ["PositionLifecycleChangeReviewPacket"]
Loading
Loading