Skip to content

feat(core): add bitemporal position reporting hierarchy - #94

Open
seonghobae wants to merge 11 commits into
developfrom
feat/position-reporting-hierarchy
Open

feat(core): add bitemporal position reporting hierarchy#94
seonghobae wants to merge 11 commits into
developfrom
feat/position-reporting-hierarchy

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Buyer-visible gap

Protected develop@9e3e4847510e1e612b48474ba42b177b8ed824df separates Job, Position and Assignment and models organization-unit hierarchy, but it has no position-to-position solid-line reporting relationship. A commercial HRIS therefore cannot reconstruct which Position a seat reports to at one business date and system-knowledge cutoff without incorrectly deriving supervision from Person/Assignment or organization-unit parentage.

This Orgmetra-only slice defines a tenant-scoped, bitemporal position-reporting contract. Managerial hierarchy is Position-to-Position, not Person-to-Person; Assignment remains an independent multiple-membership fact.

RED → root-cause implementation

  • RED 36f8f7d0605688c95ddebdc6d6f513eb81d4e144 defines deterministic subordinate→manager evidence, tenant isolation, effective/system-time visibility, one visible solid-line manager per subordinate, staffable endpoint seats, cycle/self-report rejection, trust-bearing runtime-type hardening, and redacted routine representation before production position_reporting exists.
  • Foundation CI run 32616830004, job 97138821309, checked out that exact RED SHA and failed at the first owning boundary with ModuleNotFoundError: No module named 'orgmetra_hris_kernel.position_reporting'.
  • Root implementation f75ef9a7d785229d2e8a11fe3a5257ce40a5e0c8 adds PositionReportingRelationship, PositionReportingSnapshot, and bitemporal reconstruction over authoritative PositionVersion evidence only.
  • The first implementation then produced legitimate non-passing coverage evidence: all functional tests passed but the new module was only 95% covered. Exact UUID/type and caller-owned timezone failure boundaries were subsequently covered with realistic adversarial regressions rather than weakening the gate.
  • README, dedicated traceability, and APA-7 primary-source doctoring distinguish protected-main truth from this active PR and leave persistence/mutation/UI as later bounded work.

Review-driven public API repair

Fresh Devin review found a valid buyer-facing integration defect on the previous GREEN head: the new position-reporting contract existed only in orgmetra_hris_kernel.position_reporting; the package root and __all__ did not expose PositionReportingRelationship, PositionReportingSnapshot, PositionReportingHierarchyError, or build_position_reporting_snapshot, unlike peer HRIS-kernel contracts. Submodule-only tests hid that gap while README advertised the capability.

  • RED regression fb78cbb9f85fd1c7765b98afbcd609829dba543d adds test_position_reporting_public_api.py and requires the four governed symbols to be reachable from orgmetra_hris_kernel and listed in __all__. This regression head was immediately followed by the repair before a terminal hosted RED run materialized, so no cancelled/absent hosted execution is claimed as RED evidence.
  • Root-cause repair/current head 3f67182bb3065f2fc8fd974bfdd75a390d8a8fdc exports the existing governed types/function from the package root without duplicating implementation or moving unrelated error ownership.
  • The verified Devin BUG thread is resolved only after current-head exact-hosted GREEN evidence. The remaining current Devin observations about timezone normalization and cycle-detection complexity are informational and remain unresolved.

Governed behavior

A visible solid-line reporting edge must reference exactly one same-tenant active or open PositionVersion at the requested business/system coordinate for both subordinate and manager. One subordinate can have only one visible solid-line manager; self-reporting and cycles fail closed. Caller-defined relationship/PositionVersion/date/datetime subclasses cannot control trust-bearing identity or temporal comparisons, and caller-owned timezone behavior is resolved once and detached into a built-in UTC instant. Routine representations redact position-correlation UUIDs.

The snapshot is descriptive organizational evidence only. It does not identify the worker occupying either seat, infer a manager from Assignment, reinterpret organization-unit parentage as supervisory authority, or grant employment-decision authority.

Exact-current-head evidence

Current exact head: 3f67182bb3065f2fc8fd974bfdd75a390d8a8fdc.
Fresh live base: develop@9e3e4847510e1e612b48474ba42b177b8ed824df.
GitHub reports the PR open, ready-for-review and mergeable.

Every applicable exact-current-head hosted workflow is terminal GREEN:

  • Workforce Intelligence Quality 32619302985 — success. Job 97144896045 checked out exactly 3f67182bb3065f2fc8fd974bfdd75a390d8a8fdc, ran 186 HRIS-kernel tests, and measured 799 statements / 328 branches = exactly 100% statement and branch coverage; position_reporting.py is 94 statements / 40 branches = 100%, the package-root module is 100%, the new public-API regression passed, and clean checkout passed.
  • People API Quality 32619302996 — success.
  • Job-Analysis API Quality 32619302991 — success.
  • Foundation CI 32619302968 — success.
  • Recovery Rehearsal Quality 32619302984 — success.
  • SAST Semgrep 32619302979 — success.
  • Security Scan 32619302994 — success.

Fresh submitted review state has no qualifying independent non-author APPROVE and no CHANGES_REQUESTED. The public-API BUG thread is addressed/resolved; remaining current review threads are informational.

Scope and merge governance

Write scope is Orgmetra only. No dedicated-writer dependency repository, source, ref, workflow, settings, PR state, credential, or application table is modified, and no cross-service SQL is introduced. Persistence, authorized reporting-line mutation with immutable audit/outbox, and accessible organization-chart UI remain separate future owner slices.

This unchanged exact head has terminal applicable GREEN evidence, fresh mergeability and no verified unresolved defect, so the lane is ready for qualifying independent review. Ready-for-review is not approval and does not authorize merge. Orgmetra issue #89 remains open because GitHub reports develop as protected: true while effective required-check enforcement is disabled/empty. Keep unmerged until qualifying independent non-author approval and enforceable protected-branch policy are both present on fresh state. Immediately before any merge, refetch exact head, live base, rules/protection, reviews, threads and checks and use expected-head protection only if every live gate is satisfied. Do not self-approve, bypass protection, weaken a gate, or reuse predecessor evidence.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06cb0d13-ee73-4cd2-918b-a0ca9489db40

📥 Commits

Reviewing files that changed from the base of the PR and between 9e3e484 and 3f67182.

📒 Files selected for processing (9)
  • docs/doctoring/position-reporting-hierarchy-references.md
  • docs/traceability/position-reporting-hierarchy.md
  • packages/hris-kernel/README.md
  • packages/hris-kernel/src/orgmetra_hris_kernel/__init__.py
  • packages/hris-kernel/src/orgmetra_hris_kernel/position_reporting.py
  • packages/hris-kernel/tests/test_position_reporting.py
  • packages/hris-kernel/tests/test_position_reporting_hardening.py
  • packages/hris-kernel/tests/test_position_reporting_public_api.py
  • packages/hris-kernel/tests/test_position_reporting_timezone_failure.py
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/position-reporting-hierarchy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

github-code-quality[bot]

This comment was marked as resolved.

@seonghobae
seonghobae marked this pull request as ready for review August 23, 2026 04:09

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

Comment thread packages/hris-kernel/src/orgmetra_hris_kernel/position_reporting.py
Comment on lines +35 to +70
def _freeze_known_at(value: datetime) -> datetime:
"""Detach one caller-provided timezone offset into an exact built-in UTC instant."""
if type(value) is not datetime:
raise PositionReportingHierarchyError(
"known_at must be an exact built-in datetime.",
next_action="Use the authoritative UTC system-knowledge timestamp, then rebuild the chart.",
)
if value.tzinfo is None:
raise PositionReportingHierarchyError(
"known_at must be a timezone-aware datetime.",
next_action="Attach the authoritative timezone or convert the knowledge cutoff to UTC.",
)
try:
offset = value.utcoffset()
except Exception as exc:
raise PositionReportingHierarchyError(
"known_at timezone could not be resolved safely.",
next_action="Convert the knowledge cutoff to a fixed UTC timestamp before rebuilding the chart.",
) from exc
if type(offset) is not timedelta:
raise PositionReportingHierarchyError(
"known_at must have one concrete UTC offset.",
next_action="Convert the knowledge cutoff to a fixed UTC timestamp before rebuilding the chart.",
)
wall_time = datetime(
value.year,
value.month,
value.day,
value.hour,
value.minute,
value.second,
value.microsecond,
tzinfo=timezone.utc,
fold=value.fold,
)
return wall_time - offset

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: UTC normalization correct across offsets

_freeze_known_at at position_reporting.py:59-70 tags the caller's wall components as UTC then subtracts utcoffset(), giving the correct absolute instant (UTC = local - offset). value.fold is honored so DST-ambiguous inputs resolve consistently, and a UTC input round-trips unchanged. Not a bug.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +228 to +238
for start in manager_by_subordinate:
seen: set[UUID] = set()
current: UUID | None = start
while current is not None:
if current in seen:
raise PositionReportingHierarchyError(
"Visible solid-line position reporting relationships form a cycle in this tenant.",
next_action="Close or correct one reporting edge in the cycle, then rebuild the chart.",
)
seen.add(current)
current = manager_by_subordinate.get(current)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Cycle detection sound but redundant

The one-manager-per-subordinate guard makes manager_by_subordinate a functional graph, so the per-start forward walk terminates and flags any reachable cycle with no false positives. Self-loops are already rejected at construction. Walks over shared chains repeat work (O(n^2) worst case) but the result is correct.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@seonghobae
seonghobae marked this pull request as draft August 23, 2026 05:02
@seonghobae
seonghobae marked this pull request as ready for review August 23, 2026 05:02

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment on lines +211 to +215
if subordinate in manager_by_subordinate:
raise PositionReportingHierarchyError(
"A position resolves to more than one solid-line manager at this coordinate.",
next_action="Close or correct the superseded reporting relationship, then rebuild the chart.",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Identical duplicate edge rejected, not deduplicated

Two visible edges sharing a subordinate raise the "more than one solid-line manager" error even when they name the same manager, because the guard keys only on the subordinate. This is fail-closed behavior; a redundant identical edge is rejected rather than collapsed.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@seonghobae

Copy link
Copy Markdown
Contributor Author

@opencode-agent Please review the current unchanged head against protected develop. Local exact-head verification: all owned package suites pass at 100% statement/branch coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant