-
Notifications
You must be signed in to change notification settings - Fork 0
fix(data): bind candidate conversion recorded time to PostgreSQL transaction time #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
seonghobae
wants to merge
18
commits into
develop
Choose a base branch
from
fix/candidate-conversion-system-recorded-time
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+445
−1
Draft
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 9417a97
test(candidate-conversion): add exact system-time quality lane
seonghobae 38f19bc
fix(candidate-conversion): enforce system-recorded transaction time
seonghobae b411627
test(candidate-conversion): apply system-recorded-time repair
seonghobae e41d708
test(candidate-conversion): prove server-time happy path
seonghobae 1f442e0
docs(candidate-conversion): trace system-recorded-time repair
seonghobae b86b6ee
docs(candidate-conversion): record primary system-time reference
seonghobae df9ff53
fix(provenance): inventory candidate conversion system-time artifacts
seonghobae 5cd57ab
fix(provenance): inventory candidate conversion system-time artifacts
seonghobae be55985
chore(candidate-conversion): print exact provenance inputs
seonghobae 42490a0
chore(candidate-conversion): print exact expected manifest
seonghobae bd6d23c
fix(provenance): seal candidate conversion system-time artifacts
seonghobae f70f7fa
docs: disambiguate PostgreSQL review date timezone
seonghobae 32926f7
test(candidate-conversion): cover correction system time
seonghobae bcb98f3
fix(provenance): refresh candidate conversion test digest
seonghobae 506e0ab
test(provenance): serialize conversion events in UTC
seonghobae 9080d4c
test(provenance): assert candidate conversion SQLSTATE
seonghobae 0c4fe88
docs(provenance): record candidate conversion system time
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
86 changes: 86 additions & 0 deletions
86
.github/workflows/candidate-conversion-system-time-quality.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" | ||
| 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)" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
database/migrations/0017_candidate_conversion_system_recorded_time.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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() | ||
|
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 | ||
|
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 | ||
|
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(); | ||
|
seonghobae marked this conversation as resolved.
seonghobae marked this conversation as resolved.
seonghobae marked this conversation as resolved.
|
||
|
|
||
| COMMIT; | ||
20 changes: 20 additions & 0 deletions
20
docs/doctoring/candidate-conversion-system-recorded-time-references.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
33
docs/traceability/candidate-conversion-system-recorded-time.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.