Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f025f0b
test(candidate-conversion): reject caller-authored recorded time
seonghobae Aug 22, 2026
9417a97
test(candidate-conversion): add exact system-time quality lane
seonghobae Aug 22, 2026
38f19bc
fix(candidate-conversion): enforce system-recorded transaction time
seonghobae Aug 22, 2026
b411627
test(candidate-conversion): apply system-recorded-time repair
seonghobae Aug 22, 2026
e41d708
test(candidate-conversion): prove server-time happy path
seonghobae Aug 22, 2026
1f442e0
docs(candidate-conversion): trace system-recorded-time repair
seonghobae Aug 22, 2026
b86b6ee
docs(candidate-conversion): record primary system-time reference
seonghobae Aug 22, 2026
df9ff53
fix(provenance): inventory candidate conversion system-time artifacts
seonghobae Aug 22, 2026
5cd57ab
fix(provenance): inventory candidate conversion system-time artifacts
seonghobae Aug 22, 2026
be55985
chore(candidate-conversion): print exact provenance inputs
seonghobae Aug 22, 2026
42490a0
chore(candidate-conversion): print exact expected manifest
seonghobae Aug 22, 2026
bd6d23c
fix(provenance): seal candidate conversion system-time artifacts
seonghobae Aug 22, 2026
f70f7fa
docs: disambiguate PostgreSQL review date timezone
seonghobae Aug 22, 2026
32926f7
test(candidate-conversion): cover correction system time
seonghobae Aug 28, 2026
bcb98f3
fix(provenance): refresh candidate conversion test digest
seonghobae Aug 29, 2026
506e0ab
test(provenance): serialize conversion events in UTC
seonghobae Aug 29, 2026
9080d4c
test(provenance): assert candidate conversion SQLSTATE
seonghobae Aug 29, 2026
0c4fe88
docs(provenance): record candidate conversion system time
seonghobae Aug 29, 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
86 changes: 86 additions & 0 deletions .github/workflows/candidate-conversion-system-time-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Candidate Conversion System Time Quality

on:
pull_request:
branches:
- develop
paths:
- "database/migrations/0017_candidate_conversion_system_recorded_time.sql"
- "tests/test_candidate_conversion_system_recorded_time_postgres.sh"
- ".github/workflows/candidate-conversion-system-time-quality.yml"
Comment thread
seonghobae marked this conversation as resolved.
workflow_dispatch:

permissions:
contents: read

concurrency:
group: candidate-conversion-system-time-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
postgres_contract:
name: Candidate conversion system-recorded-time contract
runs-on: ubuntu-latest
timeout-minutes: 10
services:
postgres:
image: postgres:16.14@sha256:33f923b05f64ca54ac4401c01126a6b92afe839a0aa0a52bc5aeb5cc958e5f20
env:
POSTGRES_USER: orgmetra
POSTGRES_PASSWORD: orgmetra
POSTGRES_DB: orgmetra
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U orgmetra -d orgmetra"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
DATABASE_URL: postgresql://orgmetra:orgmetra@localhost:5432/orgmetra
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: Print deterministic candidate-conversion provenance
run: |
python - <<'PY'
import hashlib
import json
from pathlib import Path

paths = [
"database/migrations/0017_candidate_conversion_system_recorded_time.sql",
"tests/test_candidate_conversion_system_recorded_time_postgres.sh",
"tests/validate_repository.py",
"scripts/foundation-contract-core.mjs",
]
rows = []
for path_text in paths:
data = Path(path_text).read_bytes()
rows.append(
{
"path": path_text,
"sha256": hashlib.sha256(data).hexdigest(),
"bytes": len(data),
"lines": len(data.decode("utf-8").splitlines()),
}
)
print(json.dumps(rows, separators=(",", ":")))
PY
- name: Print exact expected foundation manifest
run: |
python tests/validate_repository.py --print-manifest \
| python -c 'import json,sys; print(json.dumps(json.load(sys.stdin), separators=(",", ":")))'
- name: Run candidate conversion recorded-time regression
run: bash tests/test_candidate_conversion_system_recorded_time_postgres.sh
- name: Require clean checkout
run: |
git diff --exit-code
test -z "$(git status --porcelain)"
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ All notable changes to Orgmetra will be documented in this file.
- Manifest digest, byte-count, and line-count validation with regressions preventing Python/Node foundation-artifact inventories and all executable PostgreSQL migration/contract provenance from drifting apart.
- Deterministic unfinished-work marker regressions that reject explicit TODO/TBD/FIXME markers while allowing ordinary explanatory prose.

- Candidate-worker conversion system-recorded-time enforcement: migration `0017_candidate_conversion_system_recorded_time.sql` defaults `recorded_from` to `pg_catalog.transaction_timestamp()`, rejects caller-supplied backdating with SQLSTATE `23514`, and adds a targeted PostgreSQL contract for UTC serialization, valid insertion, and bitemporal correction behavior.

### Changed

- New predictive-validity membership must use one normalized worker-level case; the three independent validity-study decision/evidence/outcome link relations are historical read surfaces only and can no longer accept new rows. A case insert also rejects a criterion observation whose recorded interval is already closed at `linked_at`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- Prevent callers from authoring candidate-to-worker system-recorded time.
--
-- candidate_worker_conversion_record.recorded_from is knowledge/system time,
-- not business-effective time. Fresh inserts therefore use the PostgreSQL
-- transaction timestamp chosen by the authoritative persistence boundary.
-- effective_from remains independently caller-supplied business time and the
-- existing governance trigger continues to bind it to the confirmed decision.

BEGIN;

SET LOCAL search_path = public, pg_catalog;

CREATE FUNCTION public.enforce_candidate_conversion_system_recorded_time()
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
RETURNS trigger
LANGUAGE plpgsql
SET search_path = pg_catalog, public, pg_temp
AS $$
BEGIN
IF NEW.recorded_from IS DISTINCT FROM pg_catalog.transaction_timestamp() THEN
Comment thread
seonghobae marked this conversation as resolved.
RAISE EXCEPTION 'candidate worker conversion recorded_from must equal system transaction time'
USING ERRCODE = '23514';
END IF;

RETURN NEW;
END;
$$;

-- PostgreSQL executes same-kind triggers in name order. The `a_` segment makes
-- system-time provenance fail closed before the broader conversion-governance
-- trigger performs foreign-key/evidence lookups.
CREATE TRIGGER candidate_conversion_a_system_recorded_time_guard
Comment thread
seonghobae marked this conversation as resolved.
BEFORE INSERT ON public.candidate_worker_conversion_record
FOR EACH ROW
EXECUTE FUNCTION public.enforce_candidate_conversion_system_recorded_time();
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.

COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Candidate conversion system-recorded-time references

## Design question

Which database clock should define Orgmetra's `recorded_from` knowledge/system time for one atomic candidate-to-worker conversion transaction?

## Primary technical source

PostgreSQL Global Development Group. (2026). *PostgreSQL 16 documentation: 9.9. Date/time functions and operators*. https://www.postgresql.org/docs/16/functions-datetime.html

The PostgreSQL 16 documentation defines `transaction_timestamp()` as the start time of the current transaction and explains that transaction-current time remains stable throughout the transaction so multiple modifications can bear one consistent timestamp. Orgmetra therefore uses transaction time, rather than caller input or `clock_timestamp()`, for the atomic hire conversion's system-recorded coordinate.

## Interpretation for Orgmetra

- `effective_from` remains the business-effective date and can differ from system time.
- `recorded_from` is database-authored knowledge time and must equal the current PostgreSQL transaction timestamp on INSERT.
- A later correction creates/opens a new system-time fact through the existing governed bitemporal path rather than backdating when Orgmetra learned the fact.
- This choice is an implementation/evidence-integrity contract, not a certification or legal-compliance claim.

Reviewed against the official PostgreSQL 16 online manual on 2026-08-23 in Asia/Seoul (2026-08-22 UTC). The repository's hosted PostgreSQL test image remains separately immutably pinned by workflow digest; this reference records the major-version semantic contract rather than asserting a particular patch release is certified.
33 changes: 33 additions & 0 deletions docs/traceability/candidate-conversion-system-recorded-time.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Candidate conversion system-recorded-time traceability

## Protected-main truth

Protected `develop@9e3e4847510e1e612b48474ba42b177b8ed824df` already separates candidate-worker conversion business time (`effective_from` / `effective_to`) from knowledge/system time (`recorded_from` / `recorded_to`) and defaults `recorded_from` to PostgreSQL transaction time. The pre-repair INSERT boundary nevertheless accepts an explicit caller-authored `recorded_from` when the broader hire/evidence chronology remains internally consistent.

## Active repair

PR #87 adds a forward migration, `database/migrations/0017_candidate_conversion_system_recorded_time.sql`, that requires every fresh `candidate_worker_conversion_record.recorded_from` to equal PostgreSQL `transaction_timestamp()` for the current transaction. It does not rewrite migration 0009 or historical rows.

The repair preserves these separate meanings:

- `effective_from`: business-effective date of the candidate-to-worker conversion;
- `recorded_from`: authoritative database transaction time at which Orgmetra learned/persisted the conversion;
- `recorded_to`: later system-time closure performed only through the existing bitemporal correction guard.

The existing candidate-conversion governance trigger remains authoritative for tenant-local candidate, Person, Employment, sealed selection decision, human confirmation, audit/outbox and correction provenance. The new trigger only closes the system-time authorship gap and intentionally runs before the broader governance trigger.

## RED → GREEN acceptance evidence

`tests/test_candidate_conversion_system_recorded_time_postgres.sh` constructs an otherwise-valid tenant-local hire decision with sealed evidence and immutable audit/outbox evidence. The RED head `9417a9783bc34ae39e82a9c1da4155b843ce5492` checked out in hosted run `32598511944`, job `97093082138`, and failed because an INSERT with `recorded_from = transaction_timestamp() - interval '2 minutes'` succeeded. The exact failure was `candidate conversion accepted caller-authored historical recorded_from`.

The repaired regression requires that same INSERT to fail with SQLSTATE `23514` and the dedicated system-time error, while a conversion that omits `recorded_from` must succeed and return `recorded_from = transaction_timestamp()` from the INSERT transaction.

## Ownership and scope

This is an Orgmetra-owned persistence repair. It does not change Keyverse, Naruon, contextual-orchestrator, Psychometrics Commons or any other dedicated-writer repository; it uses no cross-service application-table SQL and grants no new employment-decision authority.

## Status vocabulary

- Protected main: vulnerable system-time authorship boundary described above.
- Active PR #87: forward migration plus adversarial PostgreSQL regression.
- Release/certification: not claimed. Merge and release still require the repository's fresh CI/security/recovery/review gates on one integrated protected head.
Loading
Loading