Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions docs/doctoring/position-reporting-hierarchy-references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Position reporting hierarchy references

Checked 2026-08-23. These sources inform the active PR #94 design without claiming that an external standard mandates Orgmetra's exact internal schema.

## Primary and authoritative sources

HR Open Standards Consortium. (2026). *About HR Open*. https://www.hropenstandards.org/about-hr-open

- HR Open describes its specifications as voluntary consensus standards for human-resource-related data exchange and interoperability. Orgmetra therefore keeps the new reporting contract modular and does not bind internal Position reporting truth to a vendor-specific worker-manager payload. This source does **not** establish that a particular `reports_to` field or table is mandatory.

Python Software Foundation. (2026). *datetime — Basic date and time types* (Python 3.14 documentation). https://docs.python.org/3.14/library/datetime.html

- The official `datetime` contract states that timezone-aware behavior delegates to `tzinfo.utcoffset()` and that an unknown offset may be represented by `None`. The reporting snapshot boundary therefore resolves the caller-owned offset once, normalizes it to a built-in UTC instant, and fails closed when the offset is absent or resolution raises, instead of repeatedly executing caller-owned timezone behavior during bitemporal comparisons.

## Internal design constraint

Protected Orgmetra architecture is the authoritative source for the distinction among Job, Position and Assignment. PR #94 adds only the missing Position-to-Position solid-line relationship reconstruction. It does not infer a manager Person from an Assignment and does not reinterpret organization-unit parentage as supervisory authority.
41 changes: 41 additions & 0 deletions docs/traceability/position-reporting-hierarchy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Position reporting hierarchy traceability

## Protected-main truth and parent adoption

The feature was originally proved against `develop@9e3e4847510e1e612b48474ba42b177b8ed824df`, where Orgmetra separated Job, Position and Assignment and stored organization-unit parentage but had no position-to-position reporting fact. Current protected truth is `develop@eb9757f8649aaad026a9865508d9aad50c1a7a4f`; this branch adopts that protected revision non-destructively while preserving the position-reporting delta. Protected #161 consolidates repository workflows but does not add authoritative position-reporting persistence or mutation, so the buyer gap remains after parent adoption.

## Active PR

PR #94 adds an in-memory HRIS-kernel contract for bitemporal solid-line Position reporting. `PositionReportingRelationship` binds one subordinate Position to one manager Position with tenant, effective/business-time, and system-recorded-time scope. `build_position_reporting_snapshot(...)` reconstructs one tenant's hierarchy at an explicit coordinate and returns deterministic subordinate-to-manager pairs.

The active contract fails closed when:

- one subordinate resolves to two visible solid-line managers;
- either endpoint does not resolve to exactly one same-tenant `active` or `open` Position version;
- a position reports to itself or the visible graph contains a cycle;
- caller-defined relationship/position/date/datetime runtime subclasses attempt to control trust-bearing comparisons;
- the system knowledge timestamp is naive, has no concrete UTC offset, or its timezone implementation raises during offset resolution.

Routine representations redact position-correlation UUIDs. The snapshot is descriptive organizational evidence, not a Person-manager link and not employment-decision authority.

## RED and repair evidence

- RED contract commit: `36f8f7d0605688c95ddebdc6d6f513eb81d4e144`.
- Foundation CI run `32616830004`, job `97138821309`, checked out that exact SHA and failed at `ModuleNotFoundError: No module named 'orgmetra_hris_kernel.position_reporting'` after the existing 171-test collection reached the missing owner boundary.
- Root implementation commit: `f75ef9a7d785229d2e8a11fe3a5257ce40a5e0c8`.
- The first implementation run then exposed an exact-coverage defect rather than a product-behavior failure: Workforce Intelligence run `32616862967`, job `97138902601`, passed all 181 tests but reported 95% coverage for the new module with missing trust-boundary branches.
- Follow-up adversarial regressions cover invalid UUID identity, unresolved/raising timezone offsets, and validation-bypassing PositionVersion runtime subtypes. The production timezone failure path is no longer excluded from coverage.

Only exact-current-head terminal workflow results may be treated as final GREEN evidence.

## Accepted architecture

Reporting authority belongs to Position, not Person. Assignment remains the independent, potentially multiple-membership fact that connects a person/employment to a seat. Organization-unit parentage remains a separate structural hierarchy and must not be repurposed as a manager relationship.

## Planned / not yet production-complete

PR #94 deliberately does **not** claim authoritative persistence or mutation. A later bounded owner slice must provide a normalized tenant-qualified persistence model, immutable audit/outbox evidence for reporting-line changes, bitemporal write/correction semantics, purpose-bound authorization, and database-level integrity without duplicating Person or Assignment data. UI organization-chart work is also separate and must receive Product Design/Figma/Storybook/accessibility evidence when it enters scope.

## Out of scope

No Keyverse, Naruon, contextual-orchestrator, Semantic Data Portal, or other dedicated-writer repository is modified. No cross-service application-table SQL is introduced.
5 changes: 4 additions & 1 deletion packages/hris-kernel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ Use this package to:
6. Build a CloudEvents 1.0-compatible audit/outbox envelope that carries tenant, actor, purpose, reason, evidence version, result, and accountable human-confirmation references without copying mutable HR payload fields into a shadow system of record.
7. Build a deterministic `JobAnalysisSnapshot` that links observable Tasks to explicit KSAO requirements, retains source/version/digest provenance, and optionally carries historical Functional Job Analysis Data/People/Things codes without confusing Job with Position or Assignment.
8. Build a deterministic `WorkforceCompositionSnapshot` for one tenant, effective day, and recorded-time cutoff with distinct-person headcount, reportable employment count, staffed assignment count/FTE, unassigned-person count, and status counts without serializing row-level person, employment, position, or assignment identifiers.
9. Build a deterministic solid-line `PositionReportingSnapshot` for one tenant, effective day, and system-knowledge cutoff without deriving supervision from Person, Assignment, or organization-unit parentage.

Every historical reconstruction and portfolio/capacity decision requires an explicit `tenant_record_id`. A colliding durable identifier from another tenant is ignored rather than treated as local employment truth.

`PositionReportingRelationship` represents managerial structure between durable Position seats. Both endpoints must resolve to exactly one same-tenant `active` or `open` `PositionVersion` at the requested business/system coordinate. One subordinate can have only one visible solid-line manager, self-reporting and cycles fail closed, and caller-defined runtime subclasses cannot control identity or temporal comparisons. `build_position_reporting_snapshot(...)` returns deterministic subordinate-to-manager UUID pairs while its routine `repr` redacts those correlation identifiers. This is descriptive organizational evidence only: it neither identifies the worker occupying a seat nor grants employment-decision authority. Persistence and reporting-line mutation remain separate authoritative write-boundary work.

`WorkforceCompositionSnapshot` is descriptive reporting evidence, not an employment recommendation or decision. It derives `active` and `leave` workforce composition from the same authoritative bitemporal facts and assignment-integrity rules used by the HRIS kernel. Concurrent employments count one person once for headcount while employment count and staffed FTE retain the actual portfolio shape. Contradictory visible versions, duplicate assignment identities, invalid coverage, and over-allocation fail closed instead of becoming plausible-looking metrics. Canonical snapshot JSON carries only the opaque tenant identifier, report coordinate, aggregate metrics, and schema version; `content_digest()` addresses those exact UTF-8 bytes with SHA-256.

`AuditOutboxEvent` fails closed on runtime type confusion, reserved nil UUID identities, ambiguous occurrence time, one-word/noncanonical source-service identifiers, malformed event types, free-text data placed in opaque-reference fields, noncanonical purpose/reason/result codes, whitespace-bearing evidence-version tokens, and missing confirmation for high-impact events. Source services use two-or-more-word `snake_case`; event types use the lower-case `orgmetra.<context>.<event>` namespace; resource, actor, and confirmation identifiers use namespaced opaque references rather than human-readable payload text.
Expand All @@ -25,4 +28,4 @@ Every historical reconstruction and portfolio/capacity decision requires an expl

`AuditOutboxEvent.canonical_json()` is the exact deterministic JSON text that the owning service persists. `AuditOutboxEvent.content_digest()` is SHA-256 over the UTF-8 bytes of that exact text. Callers must not independently serialize `to_cloudevent()` with library defaults and then assume the digest still addresses the stored representation. The Orgmetra PostgreSQL persistence boundary in migration `0003_audit_outbox_persistence.sql` reparses and allowlists that envelope, verifies tenant/event identity and high-impact confirmation, recomputes the digest over the supplied bytes, writes immutable `audit_event_record` evidence, and creates separate `outbox_delivery_record` transport state. The owning service calls `record_audit_outbox_event(...)` inside the same transaction as its business mutation.

This kernel itself does not talk to PostgreSQL, Keyverse, O*NET, or any other service. Persistence, authorization, source retrieval, and UI stay at their adapter boundaries. Production dispatcher claiming, retry scheduling, lease-expiry recovery, retention/export, external delivery receipts, job-analysis persistence, SME workflow, workforce-report authorization/presentation, and selection-validity computation remain separately proven integration/operability work.
This kernel itself does not talk to PostgreSQL, Keyverse, O*NET, or any other service. Persistence, authorization, source retrieval, and UI stay at their adapter boundaries. Production dispatcher claiming, retry scheduling, lease-expiry recovery, retention/export, external delivery receipts, job-analysis persistence, SME workflow, workforce-report authorization/presentation, position-reporting persistence/mutation, and selection-validity computation remain separately proven integration/operability work.
10 changes: 10 additions & 0 deletions packages/hris-kernel/src/orgmetra_hris_kernel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
TaskKSAOLink,
)
from orgmetra_hris_kernel.organization import validate_organization_hierarchy
from orgmetra_hris_kernel.position_reporting import (
PositionReportingHierarchyError,
PositionReportingRelationship,
PositionReportingSnapshot,
build_position_reporting_snapshot,
)
from orgmetra_hris_kernel.resolution import resolve_bitemporal_facts, resolve_single_valued_fact
from orgmetra_hris_kernel.workforce import (
WorkforceCompositionSnapshot,
Expand All @@ -70,13 +76,17 @@
"OrganizationHierarchyError",
"OrganizationUnitVersion",
"PositionCoverageError",
"PositionReportingHierarchyError",
"PositionReportingRelationship",
"PositionReportingSnapshot",
"PositionSeatError",
"PositionVersion",
"RecordedInterval",
"SingleValuedFactError",
"TaskEvidence",
"TaskKSAOLink",
"WorkforceCompositionSnapshot",
"build_position_reporting_snapshot",
"build_workforce_composition_snapshot",
"close_recorded_interval",
"resolve_bitemporal_facts",
Expand Down
Loading
Loading