From 371a0f9d18530e3c0755c2efa5842b0c3cc44095 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 04:57:43 -0700 Subject: [PATCH 01/18] test(candidate-withdrawal): define governed withdrawal evidence boundary --- tests/test_candidate_withdrawal_postgres.sh | 254 ++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 tests/test_candidate_withdrawal_postgres.sh diff --git a/tests/test_candidate_withdrawal_postgres.sh b/tests/test_candidate_withdrawal_postgres.sh new file mode 100644 index 000000000..d5193b908 --- /dev/null +++ b/tests/test_candidate_withdrawal_postgres.sh @@ -0,0 +1,254 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${DATABASE_URL:=postgresql://orgmetra:orgmetra@localhost:5432/orgmetra}" + +for migration in database/migrations/*.sql; do + psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -f "${migration}" +done + +psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO tenant_record (tenant_record_id, tenant_reference) +VALUES + ('10000000-0000-7000-8000-000000000001', 'tenant_alpha'), + ('20000000-0000-7000-8000-000000000001', 'tenant_beta'); + +INSERT INTO candidate_profile ( + tenant_record_id, candidate_profile_id, application_status_code, recorded_from +) VALUES + ( + '10000000-0000-7000-8000-000000000001', + '10000000-0000-7000-8000-000000000011', + 'legacy_unscoped', + TIMESTAMPTZ '2026-08-21 09:00:00+00' + ), + ( + '20000000-0000-7000-8000-000000000001', + '20000000-0000-7000-8000-000000000011', + 'legacy_unscoped', + TIMESTAMPTZ '2026-08-21 09:00:00+00' + ); + +INSERT INTO job_profile (tenant_record_id, job_profile_id, recorded_from) +VALUES + ( + '10000000-0000-7000-8000-000000000001', + '10000000-0000-7000-8000-000000000021', + TIMESTAMPTZ '2026-08-21 09:00:00+00' + ), + ( + '20000000-0000-7000-8000-000000000001', + '20000000-0000-7000-8000-000000000021', + TIMESTAMPTZ '2026-08-21 09:00:00+00' + ); + +INSERT INTO candidate_application_record ( + tenant_record_id, candidate_application_record_id, candidate_profile_id, + job_profile_id, requisition_reference, submitted_at, recorded_from +) VALUES + ( + '10000000-0000-7000-8000-000000000001', + '10000000-0000-7000-8000-000000000051', + '10000000-0000-7000-8000-000000000011', + '10000000-0000-7000-8000-000000000021', + 'requisition:11111111-1111-4111-8111-111111111111', + TIMESTAMPTZ '2026-08-21 09:10:00+00', + TIMESTAMPTZ '2026-08-21 09:10:01+00' + ), + ( + '20000000-0000-7000-8000-000000000001', + '20000000-0000-7000-8000-000000000051', + '20000000-0000-7000-8000-000000000011', + '20000000-0000-7000-8000-000000000021', + 'requisition:22222222-2222-4222-8222-222222222222', + TIMESTAMPTZ '2026-08-21 09:10:00+00', + TIMESTAMPTZ '2026-08-21 09:10:01+00' + ); +SQL + +record_event() { + local event_id="$1" + local outbox_id="$2" + local tenant_id="$3" + local subject="$4" + local actor="$5" + local evidence="$6" + local event_time="$7" + + psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 \ + --set=event_id="${event_id}" \ + --set=outbox_id="${outbox_id}" \ + --set=tenant_id="${tenant_id}" \ + --set=subject="${subject}" \ + --set=actor="${actor}" \ + --set=evidence="${evidence}" \ + --set=event_time="${event_time}" <<'SQL' +WITH envelope AS ( + SELECT jsonb_build_object( + 'specversion', '1.0', + 'id', :'event_id', + 'source', 'urn:orgmetra:talent_acquisition', + 'type', 'orgmetra.candidate.application_withdrawn', + 'subject', :'subject', + 'time', :'event_time', + 'datacontenttype', 'application/json', + 'orgmetratenant', :'tenant_id', + 'orgmetraactor', :'actor', + 'orgmetrapurpose', 'candidate_withdrawal', + 'orgmetrareason', 'candidate_requested', + 'orgmetraevidence', :'evidence', + 'data', jsonb_build_object( + 'high_impact', false, + 'result_code', 'application_withdrawn' + ) + )::text AS canonical_event_json +), payload AS ( + SELECT + canonical_event_json, + encode(digest(convert_to(canonical_event_json, 'UTF8'), 'sha256'), 'hex') + AS event_digest + FROM envelope +) +SELECT record_audit_outbox_event( + :'tenant_id'::uuid, + :'event_id'::uuid, + :'outbox_id'::uuid, + canonical_event_json, + event_digest, + 'talent_acquisition_events' +) +FROM payload; +SQL +} + +record_event \ + '70000000-0000-7000-8000-000000000001' \ + '71000000-0000-7000-8000-000000000001' \ + '10000000-0000-7000-8000-000000000001' \ + 'candidate_withdrawal_record:72000000-0000-7000-8000-000000000001' \ + 'candidate:73000000-0000-4000-8000-000000000001' \ + 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000001' \ + '2026-08-21T09:20:00Z' + +psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_withdrawal_record ( + tenant_record_id, + candidate_withdrawal_record_id, + candidate_application_record_id, + initiating_actor_reference, + identity_resolution_reference, + identity_resolution_digest, + withdrawal_evidence_reference, + withdrawal_evidence_digest, + evidence_version, + withdrawn_at, + audit_event_record_id, + recorded_at +) VALUES ( + '10000000-0000-7000-8000-000000000001', + '72000000-0000-7000-8000-000000000001', + '10000000-0000-7000-8000-000000000051', + 'candidate:73000000-0000-4000-8000-000000000001', + 'identity_resolution:75000000-0000-4000-8000-000000000001', + repeat('a', 64), + 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000001', + repeat('b', 64), + 1, + TIMESTAMPTZ '2026-08-21 09:20:00+00', + '70000000-0000-7000-8000-000000000001', + TIMESTAMPTZ '2026-08-21 09:20:01+00' +); +SQL + +persisted_count="$(psql "${DATABASE_URL}" -Atqc " +SELECT count(*) +FROM candidate_withdrawal_record +WHERE tenant_record_id = '10000000-0000-7000-8000-000000000001'::uuid + AND candidate_application_record_id = '10000000-0000-7000-8000-000000000051'::uuid; +")" +if [[ "${persisted_count}" != "1" ]]; then + echo "governed candidate withdrawal was not persisted exactly once" >&2 + exit 1 +fi + +set +e +duplicate_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_withdrawal_record ( + tenant_record_id, candidate_withdrawal_record_id, + candidate_application_record_id, initiating_actor_reference, + identity_resolution_reference, identity_resolution_digest, + withdrawal_evidence_reference, withdrawal_evidence_digest, + evidence_version, withdrawn_at, audit_event_record_id, recorded_at +) VALUES ( + '10000000-0000-7000-8000-000000000001', + '72000000-0000-7000-8000-000000000002', + '10000000-0000-7000-8000-000000000051', + 'candidate:73000000-0000-4000-8000-000000000001', + 'identity_resolution:75000000-0000-4000-8000-000000000001', + repeat('a', 64), + 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000001', + repeat('b', 64), + 1, + TIMESTAMPTZ '2026-08-21 09:21:00+00', + '70000000-0000-7000-8000-000000000001', + TIMESTAMPTZ '2026-08-21 09:21:01+00' +); +SQL +} 2>&1)" +duplicate_status=$? +set -e +if [[ ${duplicate_status} -eq 0 ]]; then + echo "candidate application accepted more than one withdrawal" >&2 + exit 1 +fi +if [[ "${duplicate_output}" != *"candidate_withdrawal_application_unique"* ]]; then + echo "duplicate withdrawal failed for an unexpected reason: ${duplicate_output}" >&2 + exit 1 +fi + +set +e +rewrite_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +UPDATE candidate_withdrawal_record +SET initiating_actor_reference = 'candidate:73000000-0000-4000-8000-000000000099' +WHERE candidate_withdrawal_record_id = '72000000-0000-7000-8000-000000000001'; +SQL +} 2>&1)" +rewrite_status=$? +set -e +if [[ ${rewrite_status} -eq 0 || "${rewrite_output}" != *"candidate withdrawal evidence is append-only"* ]]; then + echo "candidate withdrawal evidence was rewritable or failed unexpectedly: ${rewrite_output}" >&2 + exit 1 +fi + +set +e +staff_actor_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_withdrawal_record ( + tenant_record_id, candidate_withdrawal_record_id, + candidate_application_record_id, initiating_actor_reference, + identity_resolution_reference, identity_resolution_digest, + withdrawal_evidence_reference, withdrawal_evidence_digest, + evidence_version, withdrawn_at, audit_event_record_id, recorded_at +) VALUES ( + '20000000-0000-7000-8000-000000000001', + '82000000-0000-7000-8000-000000000001', + '20000000-0000-7000-8000-000000000051', + 'staff:83000000-0000-4000-8000-000000000001', + 'identity_resolution:85000000-0000-4000-8000-000000000001', + repeat('c', 64), + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000001', + repeat('d', 64), + 1, + TIMESTAMPTZ '2026-08-21 09:20:00+00', + '80000000-0000-7000-8000-000000000001', + TIMESTAMPTZ '2026-08-21 09:20:01+00' +); +SQL +} 2>&1)" +staff_actor_status=$? +set -e +if [[ ${staff_actor_status} -eq 0 || "${staff_actor_output}" != *"candidate_withdrawal_actor_reference_check"* ]]; then + echo "staff actor could masquerade as candidate withdrawal: ${staff_actor_output}" >&2 + exit 1 +fi + +echo "candidate withdrawal governance contract passed" From a9107d6ad9058e4b2aae54671c881de91f072b21 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 04:57:54 -0700 Subject: [PATCH 02/18] test(candidate-withdrawal): wire exact-head PostgreSQL regression --- .../candidate-withdrawal-quality.yml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/candidate-withdrawal-quality.yml diff --git a/.github/workflows/candidate-withdrawal-quality.yml b/.github/workflows/candidate-withdrawal-quality.yml new file mode 100644 index 000000000..9e03ffa56 --- /dev/null +++ b/.github/workflows/candidate-withdrawal-quality.yml @@ -0,0 +1,62 @@ +name: Candidate Withdrawal Quality + +on: + pull_request: + branches: + - develop + - feat/normalized-candidate-application + paths: + - "database/migrations/**" + - "tests/test_candidate_withdrawal_postgres.sh" + - ".github/workflows/candidate-withdrawal-quality.yml" + - "docs/adr/0027-governed-candidate-withdrawal.md" + - "docs/traceability/candidate-withdrawal.md" + - "docs/doctoring/candidate-withdrawal-references.md" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: candidate-withdrawal-quality-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + postgres_contract: + name: Candidate withdrawal PostgreSQL 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: Prove test syntax + run: bash -n tests/test_candidate_withdrawal_postgres.sh + - name: Prove governed candidate withdrawal persistence + run: bash tests/test_candidate_withdrawal_postgres.sh + - name: Prove validation is read-only + run: | + git diff --exit-code + test -z "$(git status --porcelain)" From c3531662816cd3bf294ad75406edab913c172df2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 04:58:50 -0700 Subject: [PATCH 03/18] feat(candidate-withdrawal): persist candidate-initiated immutable evidence --- .../0015_candidate_withdrawal_governance.sql | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 database/migrations/0015_candidate_withdrawal_governance.sql diff --git a/database/migrations/0015_candidate_withdrawal_governance.sql b/database/migrations/0015_candidate_withdrawal_governance.sql new file mode 100644 index 000000000..6333992ca --- /dev/null +++ b/database/migrations/0015_candidate_withdrawal_governance.sql @@ -0,0 +1,184 @@ +-- Govern candidate-initiated application withdrawal without reusing raw workflow +-- stage codes for a terminal action. The application layer remains responsible +-- for authenticating the candidate; this persistence boundary requires the +-- resulting identity-resolution evidence, candidate actor, withdrawal evidence, +-- and exact immutable audit/outbox correlation to agree before accepting a row. + +BEGIN; + +SET LOCAL search_path = public, pg_catalog; + +CREATE TABLE candidate_withdrawal_record ( + tenant_record_id uuid NOT NULL REFERENCES public.tenant_record(tenant_record_id), + candidate_withdrawal_record_id uuid PRIMARY KEY, + candidate_application_record_id uuid NOT NULL, + initiating_actor_reference text NOT NULL, + identity_resolution_reference text NOT NULL, + identity_resolution_digest text NOT NULL, + withdrawal_evidence_reference text NOT NULL, + withdrawal_evidence_digest text NOT NULL, + evidence_version integer NOT NULL, + withdrawn_at timestamptz NOT NULL, + audit_event_record_id uuid NOT NULL, + recorded_at timestamptz NOT NULL DEFAULT pg_catalog.transaction_timestamp(), + CONSTRAINT candidate_withdrawal_record_id_operational_check + CHECK ( + candidate_withdrawal_record_id <> '00000000-0000-0000-0000-000000000000'::uuid + AND candidate_withdrawal_record_id <> 'ffffffff-ffff-ffff-ffff-ffffffffffff'::uuid + ), + CONSTRAINT candidate_withdrawal_application_tenant_fk + FOREIGN KEY (tenant_record_id, candidate_application_record_id) + REFERENCES public.candidate_application_record( + tenant_record_id, candidate_application_record_id + ), + CONSTRAINT candidate_withdrawal_audit_tenant_fk + FOREIGN KEY (tenant_record_id, audit_event_record_id) + REFERENCES public.audit_event_record(tenant_record_id, audit_event_record_id), + CONSTRAINT candidate_withdrawal_actor_reference_check + CHECK ( + initiating_actor_reference ~ + '^candidate:[A-Za-z0-9][A-Za-z0-9._~-]*$' + ), + CONSTRAINT candidate_withdrawal_identity_reference_check + CHECK ( + identity_resolution_reference ~ + '^identity_resolution:[A-Za-z0-9][A-Za-z0-9._~-]*$' + ), + CONSTRAINT candidate_withdrawal_identity_digest_check + CHECK (identity_resolution_digest ~ '^[0-9a-f]{64}$'), + CONSTRAINT candidate_withdrawal_evidence_reference_check + CHECK ( + withdrawal_evidence_reference ~ + '^candidate_withdrawal_evidence:[A-Za-z0-9][A-Za-z0-9._~-]*$' + ), + CONSTRAINT candidate_withdrawal_evidence_digest_check + CHECK (withdrawal_evidence_digest ~ '^[0-9a-f]{64}$'), + CONSTRAINT candidate_withdrawal_evidence_version_check + CHECK (evidence_version >= 1 AND evidence_version <= 1000000), + CONSTRAINT candidate_withdrawal_recorded_order_check + CHECK (withdrawn_at <= recorded_at), + CONSTRAINT candidate_withdrawal_tenant_identity_unique + UNIQUE (tenant_record_id, candidate_withdrawal_record_id), + CONSTRAINT candidate_withdrawal_application_unique + UNIQUE (tenant_record_id, candidate_application_record_id), + CONSTRAINT candidate_withdrawal_audit_identity_unique + UNIQUE (tenant_record_id, audit_event_record_id) +); + +CREATE FUNCTION public.validate_candidate_withdrawal_evidence() +RETURNS trigger +LANGUAGE plpgsql +SET search_path = pg_catalog, public, pg_temp +AS $$ +DECLARE + application_submitted_at timestamptz; + audit_event_envelope jsonb; + audit_event_time timestamptz; +BEGIN + SELECT application_record.submitted_at + INTO application_submitted_at + FROM public.candidate_application_record AS application_record + WHERE application_record.tenant_record_id = NEW.tenant_record_id + AND application_record.candidate_application_record_id = + NEW.candidate_application_record_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'candidate withdrawal requires a tenant-local application' + USING ERRCODE = '23503'; + END IF; + + IF NEW.withdrawn_at < application_submitted_at THEN + RAISE EXCEPTION 'candidate withdrawal cannot predate application submission' + USING ERRCODE = '23514'; + END IF; + + SELECT audit_record.canonical_event_json::jsonb + INTO audit_event_envelope + FROM public.audit_event_record AS audit_record + WHERE audit_record.tenant_record_id = NEW.tenant_record_id + AND audit_record.audit_event_record_id = NEW.audit_event_record_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'candidate withdrawal requires a tenant-local immutable audit event' + USING ERRCODE = '23503'; + END IF; + + audit_event_time := (audit_event_envelope ->> 'time')::timestamptz; + + IF audit_event_envelope ->> 'source' <> 'urn:orgmetra:talent_acquisition' + OR audit_event_envelope ->> 'type' <> 'orgmetra.candidate.application_withdrawn' + OR audit_event_envelope ->> 'subject' + <> 'candidate_withdrawal_record:' || NEW.candidate_withdrawal_record_id::text + OR audit_event_envelope ->> 'orgmetraactor' <> NEW.initiating_actor_reference + OR audit_event_envelope ->> 'orgmetrapurpose' <> 'candidate_withdrawal' + OR audit_event_envelope ->> 'orgmetrareason' <> 'candidate_requested' + OR audit_event_envelope ->> 'orgmetraevidence' <> NEW.withdrawal_evidence_reference + OR audit_event_envelope ? 'orgmetraconfirmation' + OR (audit_event_envelope #>> '{data,high_impact}')::boolean IS NOT FALSE + OR audit_event_envelope #>> '{data,result_code}' <> 'application_withdrawn' + OR audit_event_time <> NEW.withdrawn_at + OR audit_event_time > NEW.recorded_at THEN + RAISE EXCEPTION 'candidate withdrawal audit envelope does not bind exact candidate provenance' + USING ERRCODE = '23514'; + END IF; + + IF NOT EXISTS ( + SELECT 1 + FROM public.outbox_delivery_record AS delivery_record + WHERE delivery_record.tenant_record_id = NEW.tenant_record_id + AND delivery_record.audit_event_record_id = NEW.audit_event_record_id + ) THEN + RAISE EXCEPTION 'candidate withdrawal audit event requires transactional outbox delivery evidence' + USING ERRCODE = '23514'; + END IF; + + RETURN NEW; +END; +$$; + +CREATE TRIGGER candidate_withdrawal_governance_guard +BEFORE INSERT ON candidate_withdrawal_record +FOR EACH ROW +EXECUTE FUNCTION public.validate_candidate_withdrawal_evidence(); + +CREATE FUNCTION public.reject_candidate_withdrawal_mutation() +RETURNS trigger +LANGUAGE plpgsql +SET search_path = pg_catalog, public, pg_temp +AS $$ +BEGIN + RAISE EXCEPTION 'candidate withdrawal evidence is append-only' + USING ERRCODE = '55000'; +END; +$$; + +CREATE TRIGGER candidate_withdrawal_append_only_guard +BEFORE UPDATE OR DELETE ON candidate_withdrawal_record +FOR EACH ROW +EXECUTE FUNCTION public.reject_candidate_withdrawal_mutation(); + +CREATE FUNCTION public.reject_candidate_withdrawal_truncate() +RETURNS trigger +LANGUAGE plpgsql +SET search_path = pg_catalog, public, pg_temp +AS $$ +BEGIN + RAISE EXCEPTION 'candidate withdrawal evidence cannot be truncated' + USING ERRCODE = '55000'; +END; +$$; + +CREATE TRIGGER candidate_withdrawal_truncate_guard +BEFORE TRUNCATE ON candidate_withdrawal_record +FOR EACH STATEMENT +EXECUTE FUNCTION public.reject_candidate_withdrawal_truncate(); + +REVOKE TRUNCATE ON candidate_withdrawal_record FROM PUBLIC; + +ALTER TABLE candidate_withdrawal_record ENABLE ROW LEVEL SECURITY; +ALTER TABLE candidate_withdrawal_record FORCE ROW LEVEL SECURITY; +CREATE POLICY candidate_withdrawal_scope_policy ON public.candidate_withdrawal_record +USING (tenant_record_id = public.current_tenant_record_id()) +WITH CHECK (tenant_record_id = public.current_tenant_record_id()); + +COMMIT; From b93f5213218633a631be50f735936b8236ff9921 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 05:01:37 -0700 Subject: [PATCH 04/18] test(candidate-withdrawal): isolate governed happy-path evidence --- tests/test_candidate_withdrawal_postgres.sh | 89 +++++++-------------- 1 file changed, 31 insertions(+), 58 deletions(-) diff --git a/tests/test_candidate_withdrawal_postgres.sh b/tests/test_candidate_withdrawal_postgres.sh index d5193b908..901437f74 100644 --- a/tests/test_candidate_withdrawal_postgres.sh +++ b/tests/test_candidate_withdrawal_postgres.sh @@ -3,6 +3,10 @@ set -euo pipefail : "${DATABASE_URL:=postgresql://orgmetra:orgmetra@localhost:5432/orgmetra}" +# This is the executable persistence contract for candidate-initiated withdrawal. +# It applies the complete stack, proves a governed withdrawal can be recorded, +# proves the raw application-stage shortcut remains closed, and proves the +# resulting governance evidence cannot be rewritten. for migration in database/migrations/*.sql; do psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -f "${migration}" done @@ -160,7 +164,7 @@ INSERT INTO candidate_withdrawal_record ( ); SQL -persisted_count="$(psql "${DATABASE_URL}" -Atqc " +persisted_count="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " SELECT count(*) FROM candidate_withdrawal_record WHERE tenant_record_id = '10000000-0000-7000-8000-000000000001'::uuid @@ -172,37 +176,32 @@ if [[ "${persisted_count}" != "1" ]]; then fi set +e -duplicate_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' -INSERT INTO candidate_withdrawal_record ( - tenant_record_id, candidate_withdrawal_record_id, - candidate_application_record_id, initiating_actor_reference, - identity_resolution_reference, identity_resolution_digest, - withdrawal_evidence_reference, withdrawal_evidence_digest, - evidence_version, withdrawn_at, audit_event_record_id, recorded_at +raw_stage_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_application_stage_record ( + tenant_record_id, + candidate_application_stage_record_id, + candidate_application_record_id, + application_stage_code, + effective_from, + recorded_from ) VALUES ( - '10000000-0000-7000-8000-000000000001', - '72000000-0000-7000-8000-000000000002', - '10000000-0000-7000-8000-000000000051', - 'candidate:73000000-0000-4000-8000-000000000001', - 'identity_resolution:75000000-0000-4000-8000-000000000001', - repeat('a', 64), - 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000001', - repeat('b', 64), - 1, - TIMESTAMPTZ '2026-08-21 09:21:00+00', - '70000000-0000-7000-8000-000000000001', - TIMESTAMPTZ '2026-08-21 09:21:01+00' + '20000000-0000-7000-8000-000000000001', + '20000000-0000-7000-8000-000000000061', + '20000000-0000-7000-8000-000000000051', + 'withdrawn', + TIMESTAMPTZ '2026-08-21 09:20:00+00', + TIMESTAMPTZ '2026-08-21 09:20:01+00' ); SQL } 2>&1)" -duplicate_status=$? +raw_stage_status=$? set -e -if [[ ${duplicate_status} -eq 0 ]]; then - echo "candidate application accepted more than one withdrawal" >&2 +if [[ ${raw_stage_status} -eq 0 ]]; then + echo "raw application stage reintroduced unproven withdrawn state" >&2 exit 1 fi -if [[ "${duplicate_output}" != *"candidate_withdrawal_application_unique"* ]]; then - echo "duplicate withdrawal failed for an unexpected reason: ${duplicate_output}" >&2 +if [[ "${raw_stage_output}" != *"candidate_application_stage_code_check"* ]]; then + echo "raw withdrawn stage failed for an unexpected reason: ${raw_stage_output}" >&2 exit 1 fi @@ -210,45 +209,19 @@ set +e rewrite_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' UPDATE candidate_withdrawal_record SET initiating_actor_reference = 'candidate:73000000-0000-4000-8000-000000000099' -WHERE candidate_withdrawal_record_id = '72000000-0000-7000-8000-000000000001'; +WHERE tenant_record_id = '10000000-0000-7000-8000-000000000001' + AND candidate_withdrawal_record_id = '72000000-0000-7000-8000-000000000001'; SQL } 2>&1)" rewrite_status=$? set -e -if [[ ${rewrite_status} -eq 0 || "${rewrite_output}" != *"candidate withdrawal evidence is append-only"* ]]; then - echo "candidate withdrawal evidence was rewritable or failed unexpectedly: ${rewrite_output}" >&2 +if [[ ${rewrite_status} -eq 0 ]]; then + echo "candidate withdrawal evidence was rewritten in place" >&2 exit 1 fi - -set +e -staff_actor_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' -INSERT INTO candidate_withdrawal_record ( - tenant_record_id, candidate_withdrawal_record_id, - candidate_application_record_id, initiating_actor_reference, - identity_resolution_reference, identity_resolution_digest, - withdrawal_evidence_reference, withdrawal_evidence_digest, - evidence_version, withdrawn_at, audit_event_record_id, recorded_at -) VALUES ( - '20000000-0000-7000-8000-000000000001', - '82000000-0000-7000-8000-000000000001', - '20000000-0000-7000-8000-000000000051', - 'staff:83000000-0000-4000-8000-000000000001', - 'identity_resolution:85000000-0000-4000-8000-000000000001', - repeat('c', 64), - 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000001', - repeat('d', 64), - 1, - TIMESTAMPTZ '2026-08-21 09:20:00+00', - '80000000-0000-7000-8000-000000000001', - TIMESTAMPTZ '2026-08-21 09:20:01+00' -); -SQL -} 2>&1)" -staff_actor_status=$? -set -e -if [[ ${staff_actor_status} -eq 0 || "${staff_actor_output}" != *"candidate_withdrawal_actor_reference_check"* ]]; then - echo "staff actor could masquerade as candidate withdrawal: ${staff_actor_output}" >&2 +if [[ "${rewrite_output}" != *"candidate withdrawal evidence is append-only"* ]]; then + echo "withdrawal rewrite failed for an unexpected reason: ${rewrite_output}" >&2 exit 1 fi -echo "candidate withdrawal governance contract passed" +echo "candidate withdrawal persistence contract passed" From 79c1fa4a529048577dfad6dc7354849dbd916208 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 05:02:22 -0700 Subject: [PATCH 05/18] test(candidate-withdrawal): prove anti-forgery and tenant isolation --- ..._candidate_withdrawal_security_postgres.sh | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 tests/test_candidate_withdrawal_security_postgres.sh diff --git a/tests/test_candidate_withdrawal_security_postgres.sh b/tests/test_candidate_withdrawal_security_postgres.sh new file mode 100644 index 000000000..a2bd889e9 --- /dev/null +++ b/tests/test_candidate_withdrawal_security_postgres.sh @@ -0,0 +1,288 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${DATABASE_URL:=postgresql://orgmetra:orgmetra@localhost:5432/orgmetra}" + +# This security contract intentionally runs after test_candidate_withdrawal_postgres.sh +# in the same PostgreSQL service. The predecessor script owns schema/fixture setup; +# this script isolates adversarial cases so each rejection has one causal boundary. +record_event() { + local event_id="$1" + local outbox_id="$2" + local tenant_id="$3" + local subject="$4" + local actor="$5" + local evidence="$6" + local event_time="$7" + + psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 \ + --set=event_id="${event_id}" \ + --set=outbox_id="${outbox_id}" \ + --set=tenant_id="${tenant_id}" \ + --set=subject="${subject}" \ + --set=actor="${actor}" \ + --set=evidence="${evidence}" \ + --set=event_time="${event_time}" <<'SQL' +WITH envelope AS ( + SELECT jsonb_build_object( + 'specversion', '1.0', + 'id', :'event_id', + 'source', 'urn:orgmetra:talent_acquisition', + 'type', 'orgmetra.candidate.application_withdrawn', + 'subject', :'subject', + 'time', :'event_time', + 'datacontenttype', 'application/json', + 'orgmetratenant', :'tenant_id', + 'orgmetraactor', :'actor', + 'orgmetrapurpose', 'candidate_withdrawal', + 'orgmetrareason', 'candidate_requested', + 'orgmetraevidence', :'evidence', + 'data', jsonb_build_object( + 'high_impact', false, + 'result_code', 'application_withdrawn' + ) + )::text AS canonical_event_json +), payload AS ( + SELECT + canonical_event_json, + encode(digest(convert_to(canonical_event_json, 'UTF8'), 'sha256'), 'hex') + AS event_digest + FROM envelope +) +SELECT record_audit_outbox_event( + :'tenant_id'::uuid, + :'event_id'::uuid, + :'outbox_id'::uuid, + canonical_event_json, + event_digest, + 'talent_acquisition_events' +) +FROM payload; +SQL +} + +# A second otherwise-valid audit event must not allow a second withdrawal for the +# same application. Using a distinct audit ID isolates the application uniqueness +# invariant from audit-event uniqueness. +record_event \ + '70000000-0000-7000-8000-000000000002' \ + '71000000-0000-7000-8000-000000000002' \ + '10000000-0000-7000-8000-000000000001' \ + 'candidate_withdrawal_record:72000000-0000-7000-8000-000000000002' \ + 'candidate:73000000-0000-4000-8000-000000000001' \ + 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000002' \ + '2026-08-21T09:21:00Z' + +set +e +duplicate_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_withdrawal_record ( + tenant_record_id, candidate_withdrawal_record_id, + candidate_application_record_id, initiating_actor_reference, + identity_resolution_reference, identity_resolution_digest, + withdrawal_evidence_reference, withdrawal_evidence_digest, + evidence_version, withdrawn_at, audit_event_record_id, recorded_at +) VALUES ( + '10000000-0000-7000-8000-000000000001', + '72000000-0000-7000-8000-000000000002', + '10000000-0000-7000-8000-000000000051', + 'candidate:73000000-0000-4000-8000-000000000001', + 'identity_resolution:75000000-0000-4000-8000-000000000002', + repeat('c', 64), + 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000002', + repeat('d', 64), + 2, + TIMESTAMPTZ '2026-08-21 09:21:00+00', + '70000000-0000-7000-8000-000000000002', + TIMESTAMPTZ '2026-08-21 09:21:01+00' +); +SQL +} 2>&1)" +duplicate_status=$? +set -e +if [[ ${duplicate_status} -eq 0 || "${duplicate_output}" != *"candidate_withdrawal_application_unique"* ]]; then + echo "duplicate withdrawal did not fail at the application uniqueness boundary: ${duplicate_output}" >&2 + exit 1 +fi + +# A fully valid generic audit envelope with a staff actor must still be rejected by +# the withdrawal relation itself. This prevents the generic audit API from being +# used to relabel a staff-driven adverse action as candidate withdrawal. +record_event \ + '80000000-0000-7000-8000-000000000001' \ + '81000000-0000-7000-8000-000000000001' \ + '20000000-0000-7000-8000-000000000001' \ + 'candidate_withdrawal_record:82000000-0000-7000-8000-000000000001' \ + 'staff:83000000-0000-4000-8000-000000000001' \ + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000001' \ + '2026-08-21T09:20:00Z' + +set +e +staff_actor_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_withdrawal_record ( + tenant_record_id, candidate_withdrawal_record_id, + candidate_application_record_id, initiating_actor_reference, + identity_resolution_reference, identity_resolution_digest, + withdrawal_evidence_reference, withdrawal_evidence_digest, + evidence_version, withdrawn_at, audit_event_record_id, recorded_at +) VALUES ( + '20000000-0000-7000-8000-000000000001', + '82000000-0000-7000-8000-000000000001', + '20000000-0000-7000-8000-000000000051', + 'staff:83000000-0000-4000-8000-000000000001', + 'identity_resolution:85000000-0000-4000-8000-000000000001', + repeat('e', 64), + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000001', + repeat('f', 64), + 1, + TIMESTAMPTZ '2026-08-21 09:20:00+00', + '80000000-0000-7000-8000-000000000001', + TIMESTAMPTZ '2026-08-21 09:20:01+00' +); +SQL +} 2>&1)" +staff_actor_status=$? +set -e +if [[ ${staff_actor_status} -eq 0 || "${staff_actor_output}" != *"candidate_withdrawal_actor_reference_check"* ]]; then + echo "staff actor could masquerade as candidate withdrawal: ${staff_actor_output}" >&2 + exit 1 +fi + +# Candidate-shaped actor text is not enough: the immutable audit envelope must bind +# the exact evidence reference supplied by the withdrawal row. +record_event \ + '80000000-0000-7000-8000-000000000002' \ + '81000000-0000-7000-8000-000000000002' \ + '20000000-0000-7000-8000-000000000001' \ + 'candidate_withdrawal_record:82000000-0000-7000-8000-000000000002' \ + 'candidate:83000000-0000-4000-8000-000000000002' \ + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000002' \ + '2026-08-21T09:22:00Z' + +set +e +forged_evidence_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_withdrawal_record ( + tenant_record_id, candidate_withdrawal_record_id, + candidate_application_record_id, initiating_actor_reference, + identity_resolution_reference, identity_resolution_digest, + withdrawal_evidence_reference, withdrawal_evidence_digest, + evidence_version, withdrawn_at, audit_event_record_id, recorded_at +) VALUES ( + '20000000-0000-7000-8000-000000000001', + '82000000-0000-7000-8000-000000000002', + '20000000-0000-7000-8000-000000000051', + 'candidate:83000000-0000-4000-8000-000000000002', + 'identity_resolution:85000000-0000-4000-8000-000000000002', + repeat('1', 64), + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000099', + repeat('2', 64), + 1, + TIMESTAMPTZ '2026-08-21 09:22:00+00', + '80000000-0000-7000-8000-000000000002', + TIMESTAMPTZ '2026-08-21 09:22:01+00' +); +SQL +} 2>&1)" +forged_evidence_status=$? +set -e +if [[ ${forged_evidence_status} -eq 0 || "${forged_evidence_output}" != *"candidate withdrawal audit envelope does not bind exact candidate provenance"* ]]; then + echo "withdrawal accepted audit/evidence mismatch: ${forged_evidence_output}" >&2 + exit 1 +fi + +# Persist one valid Beta withdrawal so RLS can prove positive visibility in both +# tenants rather than only proving absence. +record_event \ + '80000000-0000-7000-8000-000000000003' \ + '81000000-0000-7000-8000-000000000003' \ + '20000000-0000-7000-8000-000000000001' \ + 'candidate_withdrawal_record:82000000-0000-7000-8000-000000000003' \ + 'candidate:83000000-0000-4000-8000-000000000003' \ + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000003' \ + '2026-08-21T09:23:00Z' + +psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' +INSERT INTO candidate_withdrawal_record ( + tenant_record_id, candidate_withdrawal_record_id, + candidate_application_record_id, initiating_actor_reference, + identity_resolution_reference, identity_resolution_digest, + withdrawal_evidence_reference, withdrawal_evidence_digest, + evidence_version, withdrawn_at, audit_event_record_id, recorded_at +) VALUES ( + '20000000-0000-7000-8000-000000000001', + '82000000-0000-7000-8000-000000000003', + '20000000-0000-7000-8000-000000000051', + 'candidate:83000000-0000-4000-8000-000000000003', + 'identity_resolution:85000000-0000-4000-8000-000000000003', + repeat('3', 64), + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000003', + repeat('4', 64), + 1, + TIMESTAMPTZ '2026-08-21 09:23:00+00', + '80000000-0000-7000-8000-000000000003', + TIMESTAMPTZ '2026-08-21 09:23:01+00' +); + +CREATE ROLE orgmetra_candidate_withdrawal_reader NOLOGIN NOBYPASSRLS; +GRANT USAGE ON SCHEMA public TO orgmetra_candidate_withdrawal_reader; +GRANT SELECT ON candidate_withdrawal_record TO orgmetra_candidate_withdrawal_reader; + +SET ROLE orgmetra_candidate_withdrawal_reader; + +DO $$ +DECLARE + visible_count bigint; +BEGIN + SELECT count(*) INTO visible_count FROM candidate_withdrawal_record; + IF visible_count <> 0 THEN + RAISE EXCEPTION 'missing tenant context exposed candidate withdrawal evidence: %', visible_count; + END IF; +END; +$$; + +SET orgmetra.tenant_record_id = '10000000-0000-7000-8000-000000000001'; +DO $$ +DECLARE + visible_count bigint; + wrong_tenant_count bigint; +BEGIN + SELECT count(*) INTO visible_count FROM candidate_withdrawal_record; + SELECT count(*) INTO wrong_tenant_count + FROM candidate_withdrawal_record + WHERE tenant_record_id <> '10000000-0000-7000-8000-000000000001'::uuid; + IF visible_count <> 1 OR wrong_tenant_count <> 0 THEN + RAISE EXCEPTION 'tenant Alpha withdrawal visibility was not isolated: visible=%, wrong=%', + visible_count, wrong_tenant_count; + END IF; +END; +$$; + +SET orgmetra.tenant_record_id = '20000000-0000-7000-8000-000000000001'; +DO $$ +DECLARE + visible_count bigint; + wrong_tenant_count bigint; +BEGIN + SELECT count(*) INTO visible_count FROM candidate_withdrawal_record; + SELECT count(*) INTO wrong_tenant_count + FROM candidate_withdrawal_record + WHERE tenant_record_id <> '20000000-0000-7000-8000-000000000001'::uuid; + IF visible_count <> 1 OR wrong_tenant_count <> 0 THEN + RAISE EXCEPTION 'tenant Beta withdrawal visibility was not isolated: visible=%, wrong=%', + visible_count, wrong_tenant_count; + END IF; +END; +$$; + +RESET ROLE; +SQL + +set +e +truncate_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -c 'TRUNCATE candidate_withdrawal_record;' ; } 2>&1)" +truncate_status=$? +set -e +if [[ ${truncate_status} -eq 0 || "${truncate_output}" != *"candidate withdrawal evidence cannot be truncated"* ]]; then + echo "candidate withdrawal evidence could be truncated or failed unexpectedly: ${truncate_output}" >&2 + exit 1 +fi + +echo "candidate withdrawal anti-forgery and tenant-isolation contract passed" From fb2005f01ab949237066e4d9f794856d566d0e38 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 05:02:36 -0700 Subject: [PATCH 06/18] test(candidate-withdrawal): run focused security regression --- .github/workflows/candidate-withdrawal-quality.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/candidate-withdrawal-quality.yml b/.github/workflows/candidate-withdrawal-quality.yml index 9e03ffa56..3d3ed074b 100644 --- a/.github/workflows/candidate-withdrawal-quality.yml +++ b/.github/workflows/candidate-withdrawal-quality.yml @@ -8,6 +8,7 @@ on: paths: - "database/migrations/**" - "tests/test_candidate_withdrawal_postgres.sh" + - "tests/test_candidate_withdrawal_security_postgres.sh" - ".github/workflows/candidate-withdrawal-quality.yml" - "docs/adr/0027-governed-candidate-withdrawal.md" - "docs/traceability/candidate-withdrawal.md" @@ -53,9 +54,14 @@ jobs: ORGMETRA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} run: test "$(git rev-parse HEAD)" = "$ORGMETRA_EXPECTED_HEAD_SHA" - name: Prove test syntax - run: bash -n tests/test_candidate_withdrawal_postgres.sh + run: >- + bash -n + tests/test_candidate_withdrawal_postgres.sh + tests/test_candidate_withdrawal_security_postgres.sh - name: Prove governed candidate withdrawal persistence run: bash tests/test_candidate_withdrawal_postgres.sh + - name: Prove anti-forgery and tenant isolation + run: bash tests/test_candidate_withdrawal_security_postgres.sh - name: Prove validation is read-only run: | git diff --exit-code From 40c0da95c4e73a43fc0d626a6a803e62e850b047 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 05:03:18 -0700 Subject: [PATCH 07/18] docs(candidate-withdrawal): record governed withdrawal decision --- .../adr/0027-governed-candidate-withdrawal.md | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 docs/adr/0027-governed-candidate-withdrawal.md diff --git a/docs/adr/0027-governed-candidate-withdrawal.md b/docs/adr/0027-governed-candidate-withdrawal.md new file mode 100644 index 000000000..a207af202 --- /dev/null +++ b/docs/adr/0027-governed-candidate-withdrawal.md @@ -0,0 +1,80 @@ +# ADR 0027: Govern candidate-initiated application withdrawal as immutable evidence + +- **Status:** Proposed on active stacked PR; not protected-main truth until merged +- **Date:** 2026-08-21 +- **Decision owner:** Orgmetra +- **Dependency:** ADR 0026 / PR #66 normalized candidate application + +## Context + +ADR 0026 intentionally keeps `withdrawn` out of raw application-stage persistence. A terminal value named `withdrawn` is not self-authenticating: if the persistence layer accepts the word from any staff-facing workflow, a staff caller can encode a shadow rejection that appears candidate-driven. A defensible HRIS therefore needs a distinct evidence boundary that binds one exact application, the initiating candidate identity assertion, versioned withdrawal evidence, time, tenant, and immutable audit/outbox correlation. + +NIST SP 800-63-4 and SP 800-63B-4 provide current primary guidance for digital identity and authentication. Orgmetra does not claim NIST conformance or assign an assurance level in this slice; the relevant design principle is narrower: a candidate-shaped identifier is not itself proof that the authenticated claimant is that candidate. Authentication/identity-resolution evidence must be produced by the authorized identity adapter and carried into the governed operation as evidence. + +The existing Orgmetra audit/outbox boundary uses CloudEvents-compatible envelopes. The CloudEvents project identifies v1.0.2 as the latest released core specification. PostgreSQL 16 row-level security and trigger semantics provide the local persistence mechanisms used here for tenant isolation and immutable validation. These sources inform the boundary but do not substitute for Orgmetra's product-specific controls. + +## Decision + +Migration `0015_candidate_withdrawal_governance.sql` introduces `candidate_withdrawal_record`, a tenant-owned append-only terminal fact separate from `candidate_application_stage_record`. + +A withdrawal row binds: + +1. one tenant-local `candidate_application_record`; +2. one `candidate:` initiating actor reference; +3. an `identity_resolution:` reference plus SHA-256 digest produced by the authorized identity/authentication boundary; +4. a `candidate_withdrawal_evidence:` reference plus SHA-256 digest and bounded positive evidence version; +5. the candidate-declared `withdrawn_at` instant; +6. one immutable tenant-local `audit_event_record`; and +7. the system `recorded_at` instant. + +Exactly one withdrawal may exist for one application. The row is append-only: UPDATE, DELETE, and TRUNCATE are rejected. The table forces row-level security using Orgmetra's existing tenant-context function. + +Before insert, `validate_candidate_withdrawal_evidence()` re-resolves the tenant-local application and immutable audit event. It rejects withdrawal before application submission and requires the audit envelope to bind the exact withdrawal record, candidate actor, purpose `candidate_withdrawal`, reason `candidate_requested`, withdrawal evidence reference, event time, and non-high-impact `application_withdrawn` result. A matching transactional outbox delivery record must already exist. + +The generic audit API may represent many actor namespaces. Therefore `candidate_withdrawal_record` independently restricts its initiating actor to the `candidate:` namespace. A valid generic audit envelope with a `staff:` actor cannot become a withdrawal row. + +## Identity boundary + +This migration does **not** authenticate candidates by parsing the `candidate:` string and does not directly query Keyverse or any foreign application table. The `identity_resolution_reference` and digest are evidence inputs from Orgmetra's authorized identity adapter boundary. The adapter remains responsible for resolving the authenticated principal to the application candidate under the published identity contract before writing the withdrawal transaction. + +The database then protects what it can authoritatively prove locally: tenant/application identity, candidate actor namespace, exact audit correlation, evidence shapes and versions, chronology, one-withdrawal cardinality, append-only history, outbox presence, and tenant RLS. This separation preserves standalone operation and the dedicated-writer boundary without pretending that database syntax alone establishes digital identity. + +## Relationship to employment decisions + +Candidate withdrawal is modeled as the candidate terminating their own application process, so the audit event is explicitly `high_impact=false` and carries no fabricated employer confirmation token. Employer-driven adverse or favorable outcomes remain governed by `selection_decision` and its human-confirmed evidence boundary. The raw application-stage vocabulary remains non-terminal; this migration does not re-add `withdrawn`, `rejected`, `closed`, or `hired` as ordinary stages. + +If product requirements later allow an authorized representative to withdraw on a candidate's behalf, that is a separate delegation/representation contract with its own actor and authority evidence. It must not be implemented by broadening the current `candidate:` check to `staff:`. + +## Consequences + +- Candidate withdrawal is represented without overwriting candidate identity or smuggling a terminal outcome into an ordinary workflow stage. +- Staff callers cannot record a withdrawal merely by choosing a `withdrawn` status or by creating a generic audit envelope with a staff actor. +- Audit/event time and immutable evidence remain correlated with the exact withdrawal row. +- One application cannot accumulate contradictory multiple withdrawal facts. +- Missing tenant context fails closed under forced RLS; tenant Alpha and Beta withdrawals are mutually invisible to a `NOBYPASSRLS` reader. +- The boundary remains dependent on the application layer's authorized identity adapter for actual claimant authentication and identity resolution. This ADR does not claim otherwise. + +## Rejected alternatives + +### Re-add `withdrawn` to `candidate_application_stage_record` + +Rejected because the stage row has no initiating-actor or identity-resolution provenance. That would recreate the shadow-rejection defect that ADR 0026 closed. + +### Treat candidate withdrawal as `selection_decision` + +Rejected because a candidate terminating their own application is not an employer selection decision. Reusing the high-impact employer-decision model would misstate authority and could manufacture an inappropriate human-confirmation requirement. + +### Directly query Keyverse application tables from the database trigger + +Rejected because it violates the dedicated-writer/service boundary and creates direct cross-service database coupling. Orgmetra consumes identity proof through its authorized adapter/API contract instead. + +### Trust a `candidate:` string without evidence + +Rejected because namespace syntax is correlation, not authentication. The governed row therefore requires explicit identity-resolution reference/digest evidence and exact audit binding in addition to the actor namespace. + +## Evidence + +- `tests/test_candidate_withdrawal_postgres.sh` proves the governed happy path, preserves fail-closed rejection of raw `withdrawn` stage persistence, and proves append-only history. +- `tests/test_candidate_withdrawal_security_postgres.sh` isolates one-withdrawal cardinality, valid-audit/staff-actor rejection, audit/evidence anti-forgery, positive Alpha/Beta RLS visibility, missing-context fail-closed behavior, and TRUNCATE rejection. +- `.github/workflows/candidate-withdrawal-quality.yml` checks out the exact candidate SHA, uses pinned PostgreSQL 16.14, runs both focused contracts, and requires a clean checkout afterward. +- Primary-source review and APA 7 references are recorded in `docs/doctoring/candidate-withdrawal-references.md`. From ade9da8f4d6d6438b2b18dd9fab6894291affd1d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 05:03:35 -0700 Subject: [PATCH 08/18] docs(candidate-withdrawal): trace authority and audit evidence --- docs/traceability/candidate-withdrawal.md | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/traceability/candidate-withdrawal.md diff --git a/docs/traceability/candidate-withdrawal.md b/docs/traceability/candidate-withdrawal.md new file mode 100644 index 000000000..e44f6ea49 --- /dev/null +++ b/docs/traceability/candidate-withdrawal.md @@ -0,0 +1,31 @@ +# Candidate withdrawal traceability + +## State + +**Active stacked PR only.** This document describes `feat/governed-candidate-withdrawal` stacked on PR #66. It is not protected-`develop` truth until both dependency order and merge governance are satisfied. + +## Requirement-to-evidence map + +| Requirement | Implementation evidence | Executable evidence | +|---|---|---| +| Withdrawal belongs to one exact tenant-local application | composite FK `(tenant_record_id, candidate_application_record_id)` plus insert trigger re-resolution in `0015_candidate_withdrawal_governance.sql` | `test_candidate_withdrawal_postgres.sh`; `test_candidate_withdrawal_security_postgres.sh` | +| Candidate initiation cannot be replaced by a staff workflow action | `initiating_actor_reference` accepts only `candidate:` namespace; generic audit actor must exactly match the row | `test_candidate_withdrawal_security_postgres.sh` creates a fully valid generic staff-actor audit event and proves the withdrawal row rejects it | +| Identity syntax is not treated as authentication | row requires `identity_resolution_reference` and SHA-256 digest; authorized identity adapter remains responsible for claimant→candidate resolution | ADR 0027 documents the adapter boundary; database tests prove the evidence shape is mandatory but do **not** claim external identity authentication | +| Withdrawal evidence is versioned and immutable | withdrawal evidence reference/digest, bounded `evidence_version`, append-only UPDATE/DELETE/TRUNCATE guards | both candidate-withdrawal PostgreSQL tests | +| Audit envelope binds the exact withdrawal | insert trigger requires exact subject, actor, purpose, reason, evidence reference, event time and result; transactional outbox row required | `test_candidate_withdrawal_security_postgres.sh` supplies a candidate-shaped actor with mismatched audit evidence and proves fail-closed rejection | +| Candidate withdrawal is not an employer high-impact decision | audit envelope requires `high_impact=false` and has no `orgmetraconfirmation`; employer outcomes remain in `selection_decision` | `test_candidate_withdrawal_postgres.sh`; migration trigger predicates | +| Raw ATS workflow cannot smuggle terminal withdrawal | PR #66 stage vocabulary remains non-terminal; PR #67 does not add `withdrawn` to it | `test_candidate_withdrawal_postgres.sh` attempts raw `withdrawn` stage insertion and requires `candidate_application_stage_code_check` | +| Application chronology is preserved | withdrawal cannot predate application `submitted_at`; audit time must equal `withdrawn_at` and not exceed `recorded_at` | migration trigger exercised by happy-path and anti-forgery contracts | +| One application has at most one withdrawal fact | unique `(tenant_record_id, candidate_application_record_id)` | security test uses a second otherwise-valid audit event so the duplicate fails specifically at `candidate_withdrawal_application_unique` | +| Tenant isolation fails closed | FORCE RLS with existing `current_tenant_record_id()` policy | security test uses a `NOBYPASSRLS` reader: no context sees zero; Alpha and Beta each see only their own row | +| Validation runs on exact candidate source | pinned checkout plus explicit `git rev-parse HEAD` equality proof | `.github/workflows/candidate-withdrawal-quality.yml` | + +## Explicit non-claims + +The database does not authenticate a human candidate, verify an external authenticator, or query Keyverse directly. `candidate:` is an opaque actor namespace, not proof of identity. The identity-resolution reference/digest is meaningful only when supplied by the authorized Orgmetra identity adapter after resolving the authenticated principal under the published identity contract. This slice deliberately preserves that service boundary. + +The slice also does not implement candidate self-service UI, e-mail/SMS confirmation, delegated representative withdrawal, rescission/reinstatement, or employer closure. Those require separate governed commands and buyer-visible workflow design rather than broader status codes. + +## Standards and research linkage + +NIST SP 800-63-4 and SP 800-63B-4 inform the separation between an identifier and authenticated claimant evidence. CloudEvents v1.0.2 informs the existing event envelope shape. PostgreSQL 16 `CREATE POLICY` and `CREATE TRIGGER` semantics underpin the local tenant-isolation and insert/immutability enforcement mechanisms. APA 7 references and source notes are in `docs/doctoring/candidate-withdrawal-references.md`. From cf59b3001fa58e5a978099c2a5692a03f4849fdd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 05:03:51 -0700 Subject: [PATCH 09/18] docs(candidate-withdrawal): record primary-source references --- .../candidate-withdrawal-references.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/doctoring/candidate-withdrawal-references.md diff --git a/docs/doctoring/candidate-withdrawal-references.md b/docs/doctoring/candidate-withdrawal-references.md new file mode 100644 index 000000000..95c8b44e7 --- /dev/null +++ b/docs/doctoring/candidate-withdrawal-references.md @@ -0,0 +1,39 @@ +# Candidate withdrawal primary-source references + +## Scope note + +These sources inform the design of Orgmetra's governed candidate-withdrawal boundary. They do not establish certification or conformance, and none of them is interpreted as prescribing Orgmetra's exact physical schema. The implementation remains an Orgmetra-owned product decision. + +## Design findings + +### Digital identity and authentication + +NIST SP 800-63-4 is the current final Digital Identity Guidelines revision (July 2025) and covers identity proofing, authentication, federation, and related assertions. SP 800-63B-4 is the companion final publication focused on authentication and authenticator management. For this slice, the material design consequence is that an identifier such as `candidate:` must not be treated as proof that the current claimant is that candidate. Orgmetra therefore keeps identity/authentication verification at the authorized adapter boundary and persists a reference/digest to the resulting identity-resolution evidence rather than pretending that actor-string syntax authenticates a person. + +This PR does not assign an Identity Assurance Level, Authenticator Assurance Level, or Federation Assurance Level and makes no NIST conformance claim. + +### Event interoperability + +The CloudEvents project lists v1.0.2 as the latest released core CloudEvents specification. Orgmetra's existing audit/outbox persistence already uses a CloudEvents-compatible version-1.0 envelope with product-specific governance extensions. Candidate withdrawal reuses that accepted envelope and binds the exact withdrawal subject, candidate actor, purpose, reason, evidence reference, time, and result rather than inventing a second event format. + +### PostgreSQL persistence controls + +PostgreSQL 16 `CREATE POLICY` documentation specifies that row-level security policies become effective after RLS is enabled and that `USING` controls visible rows while `WITH CHECK` controls proposed rows. The candidate-withdrawal relation follows the repository's existing FORCE-RLS tenant-context pattern and tests it using a `NOBYPASSRLS` role. + +PostgreSQL 16 `CREATE TRIGGER` documentation defines BEFORE row triggers as executing before the attempted row operation and supports TRUNCATE triggers. Orgmetra uses a BEFORE INSERT governance trigger to re-resolve local application/audit evidence and separate UPDATE/DELETE/TRUNCATE rejection triggers to preserve append-only withdrawal history. + +## APA 7 references + +Cloud Native Computing Foundation. (2022). *CloudEvents specification* (Version 1.0.2). GitHub. https://github.com/cloudevents/spec/tree/v1.0.2 + +PostgreSQL Global Development Group. (2026). *CREATE POLICY*. In *PostgreSQL 16 documentation*. https://www.postgresql.org/docs/16/sql-createpolicy.html + +PostgreSQL Global Development Group. (2026). *CREATE TRIGGER*. In *PostgreSQL 16 documentation*. https://www.postgresql.org/docs/16/sql-createtrigger.html + +Temoshok, D., Choong, Y.-Y., Galluzzo, R., LaSalle, M., Regenscheid, A., Proud-Madruga, D., Gupta, S., & Lefkovitz, N. (2025). *Digital identity guidelines* (NIST Special Publication 800-63-4). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-63-4 + +Temoshok, D., Fenton, J., Choong, Y.-Y., Lefkovitz, N., Regenscheid, A., Galluzzo, R., & Richer, J. (2025). *Digital identity guidelines: Authentication and authenticator management* (NIST Special Publication 800-63B-4). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-63B-4 + +## Source verification date + +Primary sources were rechecked on 2026-08-21. NIST identifies SP 800-63-4 and SP 800-63B-4 as final July 2025 publications; the CloudEvents repository identifies v1.0.2 as the latest released core specification; PostgreSQL URLs above are the official version-16 documentation. From 79d1f6233c3bc5d9c2b796f3eba00f69ba6051e9 Mon Sep 17 00:00:00 2001 From: seonghobae Date: Wed, 26 Aug 2026 08:57:25 +0900 Subject: [PATCH 10/18] docs(adr): index active decision records and reseal foundation manifest --- database/migrations/0014_candidate_application_core.sql | 2 +- docs/adr/0025-governed-candidate-evidence-intake.md | 3 ++- docs/adr/0026-normalized-candidate-application.md | 3 ++- docs/adr/0027-governed-candidate-withdrawal.md | 3 ++- docs/adr/README.md | 4 ++++ manifest.json | 2 +- scripts/foundation-contract-core.mjs | 7 +++++++ tests/validate_repository.py | 7 +++++++ 8 files changed, 26 insertions(+), 5 deletions(-) diff --git a/database/migrations/0014_candidate_application_core.sql b/database/migrations/0014_candidate_application_core.sql index 77dc83286..49df7e95d 100644 --- a/database/migrations/0014_candidate_application_core.sql +++ b/database/migrations/0014_candidate_application_core.sql @@ -19,7 +19,7 @@ ALTER TABLE public.position_record ADD CONSTRAINT position_record_tenant_position_job_unique UNIQUE (tenant_record_id, position_record_id, job_profile_id); --- CREATE TABLE stays search-path-qualified rather than schema-qualified because +-- Table creation stays search-path-qualified rather than schema-qualified because -- the repository foundation validator applies the same descriptive two-word -- snake_case rule to an explicit schema token. SET LOCAL search_path above keeps -- these objects owned by public without weakening that repository-wide check. diff --git a/docs/adr/0025-governed-candidate-evidence-intake.md b/docs/adr/0025-governed-candidate-evidence-intake.md index ab66b3088..e6eb982a8 100644 --- a/docs/adr/0025-governed-candidate-evidence-intake.md +++ b/docs/adr/0025-governed-candidate-evidence-intake.md @@ -1,6 +1,7 @@ # ADR 0025: Govern candidate evidence intake as reference-only evidence -- **Status:** Proposed — active PR only +- Status: Proposed +- Provenance: active PR only - **Date:** 2026-08-19 ## Context diff --git a/docs/adr/0026-normalized-candidate-application.md b/docs/adr/0026-normalized-candidate-application.md index 911eeab32..859892cb4 100644 --- a/docs/adr/0026-normalized-candidate-application.md +++ b/docs/adr/0026-normalized-candidate-application.md @@ -1,6 +1,7 @@ # ADR 0026: Normalize candidate applications away from candidate identity -- **Status:** Proposed on active PR; not protected-main truth until merged +- Status: Proposed +- Provenance: active PR; not protected-main truth until merged - **Date:** 2026-08-21 - **Decision owner:** Orgmetra diff --git a/docs/adr/0027-governed-candidate-withdrawal.md b/docs/adr/0027-governed-candidate-withdrawal.md index a207af202..4b03134b3 100644 --- a/docs/adr/0027-governed-candidate-withdrawal.md +++ b/docs/adr/0027-governed-candidate-withdrawal.md @@ -1,6 +1,7 @@ # ADR 0027: Govern candidate-initiated application withdrawal as immutable evidence -- **Status:** Proposed on active stacked PR; not protected-main truth until merged +- Status: Proposed +- Provenance: active stacked PR; not protected-main truth until merged - **Date:** 2026-08-21 - **Decision owner:** Orgmetra - **Dependency:** ADR 0026 / PR #66 normalized candidate application diff --git a/docs/adr/README.md b/docs/adr/README.md index 099a21139..30f28eb0e 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -16,3 +16,7 @@ | [0012](0012-governed-migration-handoff.md) | Governed migration handoff | Accepted on active implementation branch | | [0013](0013-governed-requisition-review-packet.md) | Governed requisition review packet | Accepted on active implementation branch | | [0014](0014-job-analysis-snapshot-persistence.md) | Persist governed job-analysis snapshots | Accepted on active implementation branch | +| [0017](0017-governed-offer-approval.md) | Governed offer approval evidence | Proposed | +| [0025](0025-governed-candidate-evidence-intake.md) | Governed candidate evidence intake | Proposed | +| [0026](0026-normalized-candidate-application.md) | Normalize candidate applications away from candidate identity | Proposed | +| [0027](0027-governed-candidate-withdrawal.md) | Govern candidate-initiated application withdrawal as immutable evidence | Proposed | diff --git a/manifest.json b/manifest.json index 97f2bab14..ce3938a39 100644 --- a/manifest.json +++ b/manifest.json @@ -1 +1 @@ -{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/audit-outbox-envelope","files":[{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"32cc4ef78d1eca557fa01731026840be01211a043eb0ada552e4e6cb9eace353","bytes":17295,"lines":76},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"c3713a12db9d00fdc10005df1f86c07965e9555eefad78ca67e994537a739d9b","bytes":17562,"lines":448},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"6ad29731ae7ee7aa5bf3a2d0bfef88894a35a2550edb2be3244d6f143d76444a","bytes":13366,"lines":85},{"path":"docs/ERD.md","sha256":"546001aa85c4fe020e0c39d881dc860daf7f69090596666fdf9092487b0725fe","bytes":6964,"lines":70},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/README.md","sha256":"f3b3b5ed3b3b31a40a0a3696abf0065e3c25879b6be50077f38ffae742b9d002","bytes":1838,"lines":18},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"595e8381dbd62e97093b11eef818af5f04d6473ac592d57e3985ffbc2210d445","bytes":28173,"lines":689},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"960306fd7cda7b982a52c4428a432d10a4f570430a5d39fb23aeca0b2ede0615","bytes":14860,"lines":386},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"918cf92fd18d81572e9bd5f5daa7f033c32731e2e13f0d00661d1c1de30b12a9","bytes":27291,"lines":638}]} +{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/audit-outbox-envelope","files":[{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"32cc4ef78d1eca557fa01731026840be01211a043eb0ada552e4e6cb9eace353","bytes":17295,"lines":76},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"c3713a12db9d00fdc10005df1f86c07965e9555eefad78ca67e994537a739d9b","bytes":17562,"lines":448},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"3da4aa7948eb15a1058d37141e57eb210d20477be42d46c740ef8d0c6c8d23a1","bytes":9245,"lines":204},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"752ce361096a627dc5b353202d7641ceb7230114b95b3ce09ef59d712db99304","bytes":7784,"lines":184},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"6ad29731ae7ee7aa5bf3a2d0bfef88894a35a2550edb2be3244d6f143d76444a","bytes":13366,"lines":85},{"path":"docs/ERD.md","sha256":"546001aa85c4fe020e0c39d881dc860daf7f69090596666fdf9092487b0725fe","bytes":6964,"lines":70},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/README.md","sha256":"3f1c4a210f6d24a683916bace220998813682d7d7cfaa7076ac63a611d02abcc","bytes":2294,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"72115b00a71594211250d5809a66496e2783a0ab20de22b629d61ad4c3206f19","bytes":28579,"lines":696},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"960306fd7cda7b982a52c4428a432d10a4f570430a5d39fb23aeca0b2ede0615","bytes":14860,"lines":386},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"d38578a0607410bccc06269b4b95edc5fb09e8984f170900d247906e8eb9160f","bytes":1856,"lines":51},{"path":"tests/test_candidate_application_postgres.sh","sha256":"034be30b17052ec748c19c1bb7e5386e2b8ade386a8a1d0dd051128051144a38","bytes":13520,"lines":377},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"3c4656304382a7449a1d5f8e476f6a901b6eb62c046942449def21508f87b852","bytes":2588,"lines":75},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"cc18b1e5e0f30752e30c4d2404580d371b604d1d7fa1bcb48584cc0c53a8f4bb","bytes":7561,"lines":227},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"6c3759815faf56dfef5ffc3431510cc5410dee3e7413a3c19196c103572261ed","bytes":11106,"lines":288},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"1e345484a215048e8e03e7c2e9ab017d5fbe31c4becfa4259574e86d98c9f50e","bytes":27711,"lines":645}]} diff --git a/scripts/foundation-contract-core.mjs b/scripts/foundation-contract-core.mjs index 1e9fb267c..f1250b5cc 100644 --- a/scripts/foundation-contract-core.mjs +++ b/scripts/foundation-contract-core.mjs @@ -68,6 +68,8 @@ export const REQUIRED_FILES = Object.freeze([ 'database/migrations/0011_criterion_observation_scope.sql', 'database/migrations/0012_people_mutation_idempotency.sql', 'database/migrations/0013_job_analysis_snapshot.sql', + 'database/migrations/0014_candidate_application_core.sql', + 'database/migrations/0015_candidate_withdrawal_governance.sql', 'packages/hris-kernel/src/orgmetra_hris_kernel/audit.py', 'packages/hris-kernel/tests/test_audit_outbox.py', 'schemas/openapi.yaml', @@ -89,6 +91,11 @@ export const REQUIRED_FILES = Object.freeze([ 'tests/test_criterion_observation_scope_postgres.sh', 'tests/test_people_mutation_idempotency_postgres.sh', 'tests/test_job_analysis_snapshot_postgres.sh', + 'tests/test_candidate_application_postgres.sh', + 'tests/test_candidate_application_rls_postgres.sh', + 'tests/test_candidate_application_decision_boundary_postgres.sh', + 'tests/test_candidate_withdrawal_postgres.sh', + 'tests/test_candidate_withdrawal_security_postgres.sh', 'tests/validate_repository.py' ]); diff --git a/tests/validate_repository.py b/tests/validate_repository.py index fe0a329ff..2647ab798 100644 --- a/tests/validate_repository.py +++ b/tests/validate_repository.py @@ -71,6 +71,8 @@ "database/migrations/0011_criterion_observation_scope.sql", "database/migrations/0012_people_mutation_idempotency.sql", "database/migrations/0013_job_analysis_snapshot.sql", + "database/migrations/0014_candidate_application_core.sql", + "database/migrations/0015_candidate_withdrawal_governance.sql", "packages/hris-kernel/src/orgmetra_hris_kernel/audit.py", "packages/hris-kernel/tests/test_audit_outbox.py", "schemas/openapi.yaml", @@ -92,6 +94,11 @@ "tests/test_criterion_observation_scope_postgres.sh", "tests/test_people_mutation_idempotency_postgres.sh", "tests/test_job_analysis_snapshot_postgres.sh", + "tests/test_candidate_application_postgres.sh", + "tests/test_candidate_application_rls_postgres.sh", + "tests/test_candidate_application_decision_boundary_postgres.sh", + "tests/test_candidate_withdrawal_postgres.sh", + "tests/test_candidate_withdrawal_security_postgres.sh", "tests/validate_repository.py", ] From f2750f48e42ea48b889c2a383d6ef0ff8f499951 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:16:27 +0900 Subject: [PATCH 11/18] fix(withdrawal): bind audit provenance fields --- CHANGELOG.md | 1 + .../0008_audit_outbox_review_hardening.sql | 49 ++++++++++++++++- .../0015_candidate_withdrawal_governance.sql | 8 +++ .../adr/0027-governed-candidate-withdrawal.md | 2 +- .../candidate-withdrawal-references.md | 2 +- docs/traceability/candidate-withdrawal.md | 2 +- manifest.json | 2 +- tests/test_candidate_withdrawal_postgres.sh | 22 ++++++-- ..._candidate_withdrawal_security_postgres.sh | 53 ++++++++++++++----- 9 files changed, 121 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f4752d7..41ad0c75a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to Orgmetra will be documented in this file. - Accepted ADRs 0001–0003 now include buyer-facing Context, Decision, and Consequences grounded in verified ISO 30400:2022, ISO 30414:2025, Uniform Guidelines (29 C.F.R. Part 1607), SIOP (2018), OpenAPI Specification v3.2.0, OpenID Connect Core 1.0 errata set 2, CloudEvents v1.0.2, Jensen and Snodgrass (1999), Snodgrass (1999), and Allen (1983) records already listed in `docs/doctoring/REFERENCES.md`. ADRs 0004 and 0005 gained APA 7th References pointers to that same bibliography without changing their Decision bodies. - Active-PR governed Job Analysis persistence/API on the canonical `JobAnalysisSnapshot` model: migration `0013_job_analysis_snapshot.sql` stores immutable tenant-scoped snapshot, Task, KSAO, Task–KSAO, FJA and write-command evidence; `POST /v1/tenants/{tenant_record_id}/job-analysis-snapshots` and matching GET enforce purpose-bound Keyverse scope, authenticated-principal actor authority, bounded/strict JSON handling, transactional Idempotency-Key serialization, parent-scope fail-closed integrity, forced RLS, and atomic audit/outbox evidence. ADR 0014 records the persistence decision while ADR 0007 remains the domain/evidence authority; validated evidence still requires accountable human review and non-LLM provenance, and the service does not make a high-impact employment decision. +- Active-PR candidate-withdrawal persistence now binds identity-resolution reference/digest, withdrawal evidence reference/digest, and evidence version into the candidate-specific immutable audit envelope as well as the append-only withdrawal fact; generic audit envelopes retain their exact existing data contract. - Active-PR `orgmetra_selection_review` packet for PII-minimized, evidence-bound human selection review: canonical operational tenant identity, UUID-backed opaque candidate/Job/sealed-evidence/reviewer references, explicit purpose/reason/evidence version, deterministic canonical JSON and SHA-256 correlation, mandatory human decision state, redacted packet repr, and provenance-paired model evidence that remains `untrusted_draft`, with exact 100% owned statement and branch coverage required by its quality gate. - Active performance-criterion scope hardening: `criterion_observation_scope_guard` rejects criterion outcomes for a Job the worker did not effectively hold at the observation date, observations before the relevant assignment, and observations outside the referenced performance cycle while preserving valid multiple-assignment cases and existing bitemporal correction semantics. The guard evaluates current-recorded facts, derives the date coordinate from `observed_at` in UTC so session `TimeZone` cannot alter the result, uses a trusted function search path, and adds no PII or automated employment decision authority. The Foundation PostgreSQL contract also rejects a closed `recorded_to` on each time-coordinate lookup and proves UTC midnight plus non-UTC session `TimeZone` boundaries. - Bitemporal tenant-scoped organization hierarchy validation that rejects visible indirect parent cycles and reuses single-valued recorded-time reconstruction before graph traversal. diff --git a/database/migrations/0008_audit_outbox_review_hardening.sql b/database/migrations/0008_audit_outbox_review_hardening.sql index 0aeec1f62..077a3630d 100644 --- a/database/migrations/0008_audit_outbox_review_hardening.sql +++ b/database/migrations/0008_audit_outbox_review_hardening.sql @@ -88,6 +88,14 @@ DECLARE 'time', 'type' ]; + candidate_withdrawal_data_keys constant text[] := ARRAY[ + 'evidence_version', + 'high_impact', + 'identity_resolution_digest', + 'identity_resolution_reference', + 'result_code', + 'withdrawal_evidence_digest' + ]; BEGIN IF public.is_operational_uuid(p_audit_event_record_id) IS NOT TRUE OR public.is_operational_uuid(p_tenant_record_id) IS NOT TRUE THEN @@ -125,7 +133,12 @@ BEGIN SELECT pg_catalog.array_agg(data_key ORDER BY data_key COLLATE "C") INTO data_keys FROM pg_catalog.jsonb_object_keys(event_data) AS data_key_set(data_key); - IF data_keys IS DISTINCT FROM ARRAY['high_impact', 'result_code']::text[] THEN + IF event_envelope ->> 'source' = 'urn:orgmetra:talent_acquisition' + AND event_envelope ->> 'type' = 'orgmetra.candidate.application_withdrawn' THEN + IF data_keys IS DISTINCT FROM candidate_withdrawal_data_keys THEN + RETURN false; + END IF; + ELSIF data_keys IS DISTINCT FROM ARRAY['high_impact', 'result_code']::text[] THEN RETURN false; END IF; @@ -169,6 +182,40 @@ BEGIN RETURN false; END IF; + IF event_envelope ->> 'source' = 'urn:orgmetra:talent_acquisition' + AND event_envelope ->> 'type' = 'orgmetra.candidate.application_withdrawn' + AND ( + pg_catalog.jsonb_typeof(event_data -> 'evidence_version') + IS DISTINCT FROM 'number' + OR (event_data ->> 'evidence_version') IS NULL + OR ( + CASE + WHEN (event_data ->> 'evidence_version') COLLATE "C" + ~ '^[1-9][0-9]{0,6}$' + THEN (event_data ->> 'evidence_version')::integer + BETWEEN 1 AND 1000000 + ELSE false + END + ) IS NOT TRUE + OR pg_catalog.jsonb_typeof(event_data -> 'identity_resolution_reference') + IS DISTINCT FROM 'string' + OR (event_data ->> 'identity_resolution_reference') IS NULL + OR (event_data ->> 'identity_resolution_reference') COLLATE "C" + !~ '^identity_resolution:[A-Za-z0-9][A-Za-z0-9._~-]*$' + OR pg_catalog.jsonb_typeof(event_data -> 'identity_resolution_digest') + IS DISTINCT FROM 'string' + OR (event_data ->> 'identity_resolution_digest') IS NULL + OR (event_data ->> 'identity_resolution_digest') COLLATE "C" + !~ '^[0-9a-f]{64}$' + OR pg_catalog.jsonb_typeof(event_data -> 'withdrawal_evidence_digest') + IS DISTINCT FROM 'string' + OR (event_data ->> 'withdrawal_evidence_digest') IS NULL + OR (event_data ->> 'withdrawal_evidence_digest') COLLATE "C" + !~ '^[0-9a-f]{64}$' + ) THEN + RETURN false; + END IF; + event_time_text := event_envelope ->> 'time'; IF event_time_text COLLATE "C" !~ '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$' THEN diff --git a/database/migrations/0015_candidate_withdrawal_governance.sql b/database/migrations/0015_candidate_withdrawal_governance.sql index 6333992ca..8a3c8f969 100644 --- a/database/migrations/0015_candidate_withdrawal_governance.sql +++ b/database/migrations/0015_candidate_withdrawal_governance.sql @@ -113,6 +113,14 @@ BEGIN OR audit_event_envelope ->> 'orgmetrapurpose' <> 'candidate_withdrawal' OR audit_event_envelope ->> 'orgmetrareason' <> 'candidate_requested' OR audit_event_envelope ->> 'orgmetraevidence' <> NEW.withdrawal_evidence_reference + OR audit_event_envelope #>> '{data,identity_resolution_reference}' + <> NEW.identity_resolution_reference + OR audit_event_envelope #>> '{data,identity_resolution_digest}' + <> NEW.identity_resolution_digest + OR audit_event_envelope #>> '{data,withdrawal_evidence_digest}' + <> NEW.withdrawal_evidence_digest + OR audit_event_envelope #>> '{data,evidence_version}' + <> NEW.evidence_version::text OR audit_event_envelope ? 'orgmetraconfirmation' OR (audit_event_envelope #>> '{data,high_impact}')::boolean IS NOT FALSE OR audit_event_envelope #>> '{data,result_code}' <> 'application_withdrawn' diff --git a/docs/adr/0027-governed-candidate-withdrawal.md b/docs/adr/0027-governed-candidate-withdrawal.md index 4b03134b3..d3dbdba9b 100644 --- a/docs/adr/0027-governed-candidate-withdrawal.md +++ b/docs/adr/0027-governed-candidate-withdrawal.md @@ -30,7 +30,7 @@ A withdrawal row binds: Exactly one withdrawal may exist for one application. The row is append-only: UPDATE, DELETE, and TRUNCATE are rejected. The table forces row-level security using Orgmetra's existing tenant-context function. -Before insert, `validate_candidate_withdrawal_evidence()` re-resolves the tenant-local application and immutable audit event. It rejects withdrawal before application submission and requires the audit envelope to bind the exact withdrawal record, candidate actor, purpose `candidate_withdrawal`, reason `candidate_requested`, withdrawal evidence reference, event time, and non-high-impact `application_withdrawn` result. A matching transactional outbox delivery record must already exist. +Before insert, `validate_candidate_withdrawal_evidence()` re-resolves the tenant-local application and immutable audit event. It rejects withdrawal before application submission and requires the audit envelope to bind the exact withdrawal record, candidate actor, identity-resolution reference/digest, withdrawal evidence reference/digest, evidence version, purpose `candidate_withdrawal`, reason `candidate_requested`, event time, and non-high-impact `application_withdrawn` result. A matching transactional outbox delivery record must already exist. The generic audit API may represent many actor namespaces. Therefore `candidate_withdrawal_record` independently restricts its initiating actor to the `candidate:` namespace. A valid generic audit envelope with a `staff:` actor cannot become a withdrawal row. diff --git a/docs/doctoring/candidate-withdrawal-references.md b/docs/doctoring/candidate-withdrawal-references.md index 95c8b44e7..1d1b2e9ae 100644 --- a/docs/doctoring/candidate-withdrawal-references.md +++ b/docs/doctoring/candidate-withdrawal-references.md @@ -14,7 +14,7 @@ This PR does not assign an Identity Assurance Level, Authenticator Assurance Lev ### Event interoperability -The CloudEvents project lists v1.0.2 as the latest released core CloudEvents specification. Orgmetra's existing audit/outbox persistence already uses a CloudEvents-compatible version-1.0 envelope with product-specific governance extensions. Candidate withdrawal reuses that accepted envelope and binds the exact withdrawal subject, candidate actor, purpose, reason, evidence reference, time, and result rather than inventing a second event format. +The CloudEvents project lists v1.0.2 as the latest released core CloudEvents specification. Orgmetra's existing audit/outbox persistence already uses a CloudEvents-compatible version-1.0 envelope with product-specific governance extensions. Candidate withdrawal reuses that accepted envelope and binds the exact withdrawal subject, candidate actor, identity-resolution reference/digest, withdrawal evidence reference/digest, evidence version, purpose, reason, time, and result rather than inventing a second event format. ### PostgreSQL persistence controls diff --git a/docs/traceability/candidate-withdrawal.md b/docs/traceability/candidate-withdrawal.md index e44f6ea49..8f16890bb 100644 --- a/docs/traceability/candidate-withdrawal.md +++ b/docs/traceability/candidate-withdrawal.md @@ -12,7 +12,7 @@ | Candidate initiation cannot be replaced by a staff workflow action | `initiating_actor_reference` accepts only `candidate:` namespace; generic audit actor must exactly match the row | `test_candidate_withdrawal_security_postgres.sh` creates a fully valid generic staff-actor audit event and proves the withdrawal row rejects it | | Identity syntax is not treated as authentication | row requires `identity_resolution_reference` and SHA-256 digest; authorized identity adapter remains responsible for claimant→candidate resolution | ADR 0027 documents the adapter boundary; database tests prove the evidence shape is mandatory but do **not** claim external identity authentication | | Withdrawal evidence is versioned and immutable | withdrawal evidence reference/digest, bounded `evidence_version`, append-only UPDATE/DELETE/TRUNCATE guards | both candidate-withdrawal PostgreSQL tests | -| Audit envelope binds the exact withdrawal | insert trigger requires exact subject, actor, purpose, reason, evidence reference, event time and result; transactional outbox row required | `test_candidate_withdrawal_security_postgres.sh` supplies a candidate-shaped actor with mismatched audit evidence and proves fail-closed rejection | +| Audit envelope binds the exact withdrawal | insert trigger requires exact subject, actor, identity-resolution reference/digest, evidence reference/digest, evidence version, purpose, reason, event time and result; transactional outbox row required | `test_candidate_withdrawal_security_postgres.sh` supplies candidate-shaped but mismatched provenance and proves fail-closed rejection | | Candidate withdrawal is not an employer high-impact decision | audit envelope requires `high_impact=false` and has no `orgmetraconfirmation`; employer outcomes remain in `selection_decision` | `test_candidate_withdrawal_postgres.sh`; migration trigger predicates | | Raw ATS workflow cannot smuggle terminal withdrawal | PR #66 stage vocabulary remains non-terminal; PR #67 does not add `withdrawn` to it | `test_candidate_withdrawal_postgres.sh` attempts raw `withdrawn` stage insertion and requires `candidate_application_stage_code_check` | | Application chronology is preserved | withdrawal cannot predate application `submitted_at`; audit time must equal `withdrawn_at` and not exceed `recorded_at` | migration trigger exercised by happy-path and anti-forgery contracts | diff --git a/manifest.json b/manifest.json index ce3938a39..02ba34625 100644 --- a/manifest.json +++ b/manifest.json @@ -1 +1 @@ -{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/audit-outbox-envelope","files":[{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"32cc4ef78d1eca557fa01731026840be01211a043eb0ada552e4e6cb9eace353","bytes":17295,"lines":76},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"c3713a12db9d00fdc10005df1f86c07965e9555eefad78ca67e994537a739d9b","bytes":17562,"lines":448},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"3da4aa7948eb15a1058d37141e57eb210d20477be42d46c740ef8d0c6c8d23a1","bytes":9245,"lines":204},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"752ce361096a627dc5b353202d7641ceb7230114b95b3ce09ef59d712db99304","bytes":7784,"lines":184},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"6ad29731ae7ee7aa5bf3a2d0bfef88894a35a2550edb2be3244d6f143d76444a","bytes":13366,"lines":85},{"path":"docs/ERD.md","sha256":"546001aa85c4fe020e0c39d881dc860daf7f69090596666fdf9092487b0725fe","bytes":6964,"lines":70},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/README.md","sha256":"3f1c4a210f6d24a683916bace220998813682d7d7cfaa7076ac63a611d02abcc","bytes":2294,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"72115b00a71594211250d5809a66496e2783a0ab20de22b629d61ad4c3206f19","bytes":28579,"lines":696},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"960306fd7cda7b982a52c4428a432d10a4f570430a5d39fb23aeca0b2ede0615","bytes":14860,"lines":386},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"d38578a0607410bccc06269b4b95edc5fb09e8984f170900d247906e8eb9160f","bytes":1856,"lines":51},{"path":"tests/test_candidate_application_postgres.sh","sha256":"034be30b17052ec748c19c1bb7e5386e2b8ade386a8a1d0dd051128051144a38","bytes":13520,"lines":377},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"3c4656304382a7449a1d5f8e476f6a901b6eb62c046942449def21508f87b852","bytes":2588,"lines":75},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"cc18b1e5e0f30752e30c4d2404580d371b604d1d7fa1bcb48584cc0c53a8f4bb","bytes":7561,"lines":227},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"6c3759815faf56dfef5ffc3431510cc5410dee3e7413a3c19196c103572261ed","bytes":11106,"lines":288},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"1e345484a215048e8e03e7c2e9ab017d5fbe31c4becfa4259574e86d98c9f50e","bytes":27711,"lines":645}]} +{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/audit-outbox-envelope","files":[{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"951e29bb143f04cb6d8b20cd5d2110f0643e39e66c3c2c50565a0954e4dc922e","bytes":17611,"lines":77},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"3da4aa7948eb15a1058d37141e57eb210d20477be42d46c740ef8d0c6c8d23a1","bytes":9245,"lines":204},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"6ad29731ae7ee7aa5bf3a2d0bfef88894a35a2550edb2be3244d6f143d76444a","bytes":13366,"lines":85},{"path":"docs/ERD.md","sha256":"546001aa85c4fe020e0c39d881dc860daf7f69090596666fdf9092487b0725fe","bytes":6964,"lines":70},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/README.md","sha256":"3f1c4a210f6d24a683916bace220998813682d7d7cfaa7076ac63a611d02abcc","bytes":2294,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"72115b00a71594211250d5809a66496e2783a0ab20de22b629d61ad4c3206f19","bytes":28579,"lines":696},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"960306fd7cda7b982a52c4428a432d10a4f570430a5d39fb23aeca0b2ede0615","bytes":14860,"lines":386},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"d38578a0607410bccc06269b4b95edc5fb09e8984f170900d247906e8eb9160f","bytes":1856,"lines":51},{"path":"tests/test_candidate_application_postgres.sh","sha256":"034be30b17052ec748c19c1bb7e5386e2b8ade386a8a1d0dd051128051144a38","bytes":13520,"lines":377},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"3c4656304382a7449a1d5f8e476f6a901b6eb62c046942449def21508f87b852","bytes":2588,"lines":75},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"1462b22ad7e0ba8c27dc021da4c83cfc634aa9902a22722ad9902357f19fb516","bytes":8320,"lines":243},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"1e345484a215048e8e03e7c2e9ab017d5fbe31c4becfa4259574e86d98c9f50e","bytes":27711,"lines":645}]} diff --git a/tests/test_candidate_withdrawal_postgres.sh b/tests/test_candidate_withdrawal_postgres.sh index 901437f74..7c8adad21 100644 --- a/tests/test_candidate_withdrawal_postgres.sh +++ b/tests/test_candidate_withdrawal_postgres.sh @@ -78,6 +78,10 @@ record_event() { local actor="$5" local evidence="$6" local event_time="$7" + local identity_reference="$8" + local identity_digest="$9" + local withdrawal_digest="${10}" + local evidence_version="${11}" psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 \ --set=event_id="${event_id}" \ @@ -86,7 +90,11 @@ record_event() { --set=subject="${subject}" \ --set=actor="${actor}" \ --set=evidence="${evidence}" \ - --set=event_time="${event_time}" <<'SQL' + --set=event_time="${event_time}" \ + --set=identity_reference="${identity_reference}" \ + --set=identity_digest="${identity_digest}" \ + --set=withdrawal_digest="${withdrawal_digest}" \ + --set=evidence_version="${evidence_version}" <<'SQL' WITH envelope AS ( SELECT jsonb_build_object( 'specversion', '1.0', @@ -103,7 +111,11 @@ WITH envelope AS ( 'orgmetraevidence', :'evidence', 'data', jsonb_build_object( 'high_impact', false, - 'result_code', 'application_withdrawn' + 'result_code', 'application_withdrawn', + 'evidence_version', :'evidence_version'::integer, + 'identity_resolution_reference', :'identity_reference', + 'identity_resolution_digest', :'identity_digest', + 'withdrawal_evidence_digest', :'withdrawal_digest' ) )::text AS canonical_event_json ), payload AS ( @@ -132,7 +144,11 @@ record_event \ 'candidate_withdrawal_record:72000000-0000-7000-8000-000000000001' \ 'candidate:73000000-0000-4000-8000-000000000001' \ 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000001' \ - '2026-08-21T09:20:00Z' + '2026-08-21T09:20:00Z' \ + 'identity_resolution:75000000-0000-4000-8000-000000000001' \ + "$(printf 'a%.0s' {1..64})" \ + "$(printf 'b%.0s' {1..64})" \ + '1' psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' INSERT INTO candidate_withdrawal_record ( diff --git a/tests/test_candidate_withdrawal_security_postgres.sh b/tests/test_candidate_withdrawal_security_postgres.sh index a2bd889e9..7c9afe0f6 100644 --- a/tests/test_candidate_withdrawal_security_postgres.sh +++ b/tests/test_candidate_withdrawal_security_postgres.sh @@ -14,6 +14,10 @@ record_event() { local actor="$5" local evidence="$6" local event_time="$7" + local identity_reference="$8" + local identity_digest="$9" + local withdrawal_digest="${10}" + local evidence_version="${11}" psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 \ --set=event_id="${event_id}" \ @@ -22,7 +26,11 @@ record_event() { --set=subject="${subject}" \ --set=actor="${actor}" \ --set=evidence="${evidence}" \ - --set=event_time="${event_time}" <<'SQL' + --set=event_time="${event_time}" \ + --set=identity_reference="${identity_reference}" \ + --set=identity_digest="${identity_digest}" \ + --set=withdrawal_digest="${withdrawal_digest}" \ + --set=evidence_version="${evidence_version}" <<'SQL' WITH envelope AS ( SELECT jsonb_build_object( 'specversion', '1.0', @@ -39,7 +47,11 @@ WITH envelope AS ( 'orgmetraevidence', :'evidence', 'data', jsonb_build_object( 'high_impact', false, - 'result_code', 'application_withdrawn' + 'result_code', 'application_withdrawn', + 'evidence_version', :'evidence_version'::integer, + 'identity_resolution_reference', :'identity_reference', + 'identity_resolution_digest', :'identity_digest', + 'withdrawal_evidence_digest', :'withdrawal_digest' ) )::text AS canonical_event_json ), payload AS ( @@ -71,7 +83,11 @@ record_event \ 'candidate_withdrawal_record:72000000-0000-7000-8000-000000000002' \ 'candidate:73000000-0000-4000-8000-000000000001' \ 'candidate_withdrawal_evidence:74000000-0000-4000-8000-000000000002' \ - '2026-08-21T09:21:00Z' + '2026-08-21T09:21:00Z' \ + 'identity_resolution:75000000-0000-4000-8000-000000000002' \ + "$(printf 'c%.0s' {1..64})" \ + "$(printf 'd%.0s' {1..64})" \ + '2' set +e duplicate_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' @@ -114,7 +130,11 @@ record_event \ 'candidate_withdrawal_record:82000000-0000-7000-8000-000000000001' \ 'staff:83000000-0000-4000-8000-000000000001' \ 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000001' \ - '2026-08-21T09:20:00Z' + '2026-08-21T09:20:00Z' \ + 'identity_resolution:85000000-0000-4000-8000-000000000001' \ + "$(printf 'e%.0s' {1..64})" \ + "$(printf 'f%.0s' {1..64})" \ + '1' set +e staff_actor_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' @@ -148,7 +168,8 @@ if [[ ${staff_actor_status} -eq 0 || "${staff_actor_output}" != *"candidate_with fi # Candidate-shaped actor text is not enough: the immutable audit envelope must bind -# the exact evidence reference supplied by the withdrawal row. +# the exact evidence reference, identity-resolution reference/digest, withdrawal +# digest, and evidence version supplied by the withdrawal row. record_event \ '80000000-0000-7000-8000-000000000002' \ '81000000-0000-7000-8000-000000000002' \ @@ -156,7 +177,11 @@ record_event \ 'candidate_withdrawal_record:82000000-0000-7000-8000-000000000002' \ 'candidate:83000000-0000-4000-8000-000000000002' \ 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000002' \ - '2026-08-21T09:22:00Z' + '2026-08-21T09:22:00Z' \ + 'identity_resolution:85000000-0000-4000-8000-000000000002' \ + "$(printf '1%.0s' {1..64})" \ + "$(printf '2%.0s' {1..64})" \ + '1' set +e forged_evidence_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' @@ -171,11 +196,11 @@ INSERT INTO candidate_withdrawal_record ( '82000000-0000-7000-8000-000000000002', '20000000-0000-7000-8000-000000000051', 'candidate:83000000-0000-4000-8000-000000000002', - 'identity_resolution:85000000-0000-4000-8000-000000000002', - repeat('1', 64), - 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000099', - repeat('2', 64), - 1, + 'identity_resolution:85000000-0000-4000-8000-000000000099', + repeat('9', 64), + 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000002', + repeat('8', 64), + 3, TIMESTAMPTZ '2026-08-21 09:22:00+00', '80000000-0000-7000-8000-000000000002', TIMESTAMPTZ '2026-08-21 09:22:01+00' @@ -198,7 +223,11 @@ record_event \ 'candidate_withdrawal_record:82000000-0000-7000-8000-000000000003' \ 'candidate:83000000-0000-4000-8000-000000000003' \ 'candidate_withdrawal_evidence:84000000-0000-4000-8000-000000000003' \ - '2026-08-21T09:23:00Z' + '2026-08-21T09:23:00Z' \ + 'identity_resolution:85000000-0000-4000-8000-000000000003' \ + "$(printf '3%.0s' {1..64})" \ + "$(printf '4%.0s' {1..64})" \ + '1' psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' INSERT INTO candidate_withdrawal_record ( From ccaabebb152a534a3bfbe074d8edeaf16c1533ff Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:36:29 +0900 Subject: [PATCH 12/18] test(withdrawal): align fixture with normalized application --- manifest.json | 2 +- tests/test_candidate_withdrawal_postgres.sh | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/manifest.json b/manifest.json index 93defe001..17ecbf95b 100644 --- a/manifest.json +++ b/manifest.json @@ -1 +1 @@ -{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/normalized-candidate-application","files":[{"path":".github/workflows/candidate-application-quality.yml","sha256":"3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931","bytes":3467,"lines":81},{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"414858744d8242130adae52fa273e9843362c81eb645baff0d8b26a3ae50ad84","bytes":18517,"lines":78},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad","bytes":13889,"lines":307},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6","bytes":14913,"lines":90},{"path":"docs/ERD.md","sha256":"f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246","bytes":8331,"lines":77},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/0026-normalized-candidate-application.md","sha256":"ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081","bytes":9945,"lines":73},{"path":"docs/adr/README.md","sha256":"19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e","bytes":2310,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/doctoring/candidate-application-references.md","sha256":"202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c","bytes":2962,"lines":24},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"docs/traceability/candidate-application-core.md","sha256":"3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14","bytes":6877,"lines":35},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"52b519e211de7a899cf10fda2e779782113949a07dabc856d6120f8d7edcf428","bytes":29711,"lines":718},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4","bytes":16371,"lines":427},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416","bytes":3954,"lines":98},{"path":"tests/test_candidate_application_postgres.sh","sha256":"69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686","bytes":14311,"lines":236},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563","bytes":4637,"lines":118},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"1462b22ad7e0ba8c27dc021da4c83cfc634aa9902a22722ad9902357f19fb516","bytes":8320,"lines":243},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"c34f5ca4fd38b8df288ecb99b48905e292b80f7bac4fa0bafe16f2e3cb85b611","bytes":29090,"lines":681}]} +{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/normalized-candidate-application","files":[{"path":".github/workflows/candidate-application-quality.yml","sha256":"3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931","bytes":3467,"lines":81},{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"414858744d8242130adae52fa273e9843362c81eb645baff0d8b26a3ae50ad84","bytes":18517,"lines":78},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad","bytes":13889,"lines":307},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6","bytes":14913,"lines":90},{"path":"docs/ERD.md","sha256":"f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246","bytes":8331,"lines":77},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/0026-normalized-candidate-application.md","sha256":"ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081","bytes":9945,"lines":73},{"path":"docs/adr/README.md","sha256":"19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e","bytes":2310,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/doctoring/candidate-application-references.md","sha256":"202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c","bytes":2962,"lines":24},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"docs/traceability/candidate-application-core.md","sha256":"3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14","bytes":6877,"lines":35},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"52b519e211de7a899cf10fda2e779782113949a07dabc856d6120f8d7edcf428","bytes":29711,"lines":718},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4","bytes":16371,"lines":427},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416","bytes":3954,"lines":98},{"path":"tests/test_candidate_application_postgres.sh","sha256":"69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686","bytes":14311,"lines":236},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563","bytes":4637,"lines":118},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"72e1376964a2d056ffa95b8078acb98df5771777ebbf6ba050a1d44b96b2f840","bytes":7914,"lines":230},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"c34f5ca4fd38b8df288ecb99b48905e292b80f7bac4fa0bafe16f2e3cb85b611","bytes":29090,"lines":681}]} diff --git a/tests/test_candidate_withdrawal_postgres.sh b/tests/test_candidate_withdrawal_postgres.sh index 7c8adad21..72e3af7e0 100644 --- a/tests/test_candidate_withdrawal_postgres.sh +++ b/tests/test_candidate_withdrawal_postgres.sh @@ -33,22 +33,9 @@ INSERT INTO candidate_profile ( TIMESTAMPTZ '2026-08-21 09:00:00+00' ); -INSERT INTO job_profile (tenant_record_id, job_profile_id, recorded_from) -VALUES - ( - '10000000-0000-7000-8000-000000000001', - '10000000-0000-7000-8000-000000000021', - TIMESTAMPTZ '2026-08-21 09:00:00+00' - ), - ( - '20000000-0000-7000-8000-000000000001', - '20000000-0000-7000-8000-000000000021', - TIMESTAMPTZ '2026-08-21 09:00:00+00' - ); - INSERT INTO candidate_application_record ( tenant_record_id, candidate_application_record_id, candidate_profile_id, - job_profile_id, requisition_reference, submitted_at, recorded_from + requisition_reference, submitted_at, recorded_from ) VALUES ( '10000000-0000-7000-8000-000000000001', From 687231aba2289242abf289370567168bbb889c05 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:39:24 +0900 Subject: [PATCH 13/18] test(withdrawal): cover provenance artifacts --- manifest.json | 2 +- scripts/foundation-contract-core.mjs | 7 ++++--- tests/validate_repository.py | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/manifest.json b/manifest.json index 17ecbf95b..d073d6ab1 100644 --- a/manifest.json +++ b/manifest.json @@ -1 +1 @@ -{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/normalized-candidate-application","files":[{"path":".github/workflows/candidate-application-quality.yml","sha256":"3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931","bytes":3467,"lines":81},{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"414858744d8242130adae52fa273e9843362c81eb645baff0d8b26a3ae50ad84","bytes":18517,"lines":78},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad","bytes":13889,"lines":307},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6","bytes":14913,"lines":90},{"path":"docs/ERD.md","sha256":"f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246","bytes":8331,"lines":77},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/0026-normalized-candidate-application.md","sha256":"ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081","bytes":9945,"lines":73},{"path":"docs/adr/README.md","sha256":"19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e","bytes":2310,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/doctoring/candidate-application-references.md","sha256":"202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c","bytes":2962,"lines":24},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"docs/traceability/candidate-application-core.md","sha256":"3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14","bytes":6877,"lines":35},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"52b519e211de7a899cf10fda2e779782113949a07dabc856d6120f8d7edcf428","bytes":29711,"lines":718},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4","bytes":16371,"lines":427},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416","bytes":3954,"lines":98},{"path":"tests/test_candidate_application_postgres.sh","sha256":"69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686","bytes":14311,"lines":236},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563","bytes":4637,"lines":118},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"72e1376964a2d056ffa95b8078acb98df5771777ebbf6ba050a1d44b96b2f840","bytes":7914,"lines":230},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"c34f5ca4fd38b8df288ecb99b48905e292b80f7bac4fa0bafe16f2e3cb85b611","bytes":29090,"lines":681}]} +{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/normalized-candidate-application","files":[{"path":".github/workflows/candidate-application-quality.yml","sha256":"3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931","bytes":3467,"lines":81},{"path":".github/workflows/candidate-withdrawal-quality.yml","sha256":"e32de389a89388c33e0bea1d88b71470f4931ff28dafb646b5f9a907063e1f7a","bytes":2449,"lines":68},{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"414858744d8242130adae52fa273e9843362c81eb645baff0d8b26a3ae50ad84","bytes":18517,"lines":78},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad","bytes":13889,"lines":307},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6","bytes":14913,"lines":90},{"path":"docs/ERD.md","sha256":"f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246","bytes":8331,"lines":77},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/0026-normalized-candidate-application.md","sha256":"ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081","bytes":9945,"lines":73},{"path":"docs/adr/0027-governed-candidate-withdrawal.md","sha256":"48d7c949989b80646f690d4185150ad50e8c31a3c29761a6be95b09849ba9d72","bytes":7850,"lines":81},{"path":"docs/adr/README.md","sha256":"19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e","bytes":2310,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/doctoring/candidate-application-references.md","sha256":"202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c","bytes":2962,"lines":24},{"path":"docs/doctoring/candidate-withdrawal-references.md","sha256":"a49be5ae37725158d7da7a82f6cc60df122f2cb086e1234bcecd5c85a41242c2","bytes":3923,"lines":39},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"docs/traceability/candidate-application-core.md","sha256":"3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14","bytes":6877,"lines":35},{"path":"docs/traceability/candidate-withdrawal.md","sha256":"49d6f47ce8130cac94e67bb0d6e13dc772b07f0f5e4b8c4a6816aa001166d046","bytes":4552,"lines":31},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"bb09a70cc1950cf0862c63d9824548d5b871eb2daf3bf9d783816c95265d3cdd","bytes":29751,"lines":719},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4","bytes":16371,"lines":427},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416","bytes":3954,"lines":98},{"path":"tests/test_candidate_application_postgres.sh","sha256":"69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686","bytes":14311,"lines":236},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563","bytes":4637,"lines":118},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"72e1376964a2d056ffa95b8078acb98df5771777ebbf6ba050a1d44b96b2f840","bytes":7914,"lines":230},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"984d4c068f9b1b9c8394fc9d1e70e909446946699d689d9dcc1b97ada8bcee3c","bytes":29130,"lines":682}]} diff --git a/scripts/foundation-contract-core.mjs b/scripts/foundation-contract-core.mjs index 8f5f5eaa4..cb8be431e 100644 --- a/scripts/foundation-contract-core.mjs +++ b/scripts/foundation-contract-core.mjs @@ -53,8 +53,10 @@ export const REQUIRED_FILES = Object.freeze([ 'docs/adr/0013-governed-requisition-review-packet.md', 'docs/adr/0014-job-analysis-snapshot-persistence.md', 'docs/adr/0026-normalized-candidate-application.md', + 'docs/adr/0027-governed-candidate-withdrawal.md', 'docs/doctoring/REFERENCES.md', 'docs/doctoring/candidate-application-references.md', + 'docs/doctoring/candidate-withdrawal-references.md', 'docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md', 'docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md', 'database/migrations/0001_foundation_schema.sql', @@ -73,7 +75,9 @@ export const REQUIRED_FILES = Object.freeze([ 'database/migrations/0014_candidate_application_core.sql', 'database/migrations/0015_candidate_withdrawal_governance.sql', 'docs/traceability/candidate-application-core.md', + 'docs/traceability/candidate-withdrawal.md', '.github/workflows/candidate-application-quality.yml', + '.github/workflows/candidate-withdrawal-quality.yml', 'packages/hris-kernel/src/orgmetra_hris_kernel/audit.py', 'packages/hris-kernel/tests/test_audit_outbox.py', 'schemas/openapi.yaml', @@ -98,9 +102,6 @@ export const REQUIRED_FILES = Object.freeze([ 'tests/test_criterion_observation_scope_postgres.sh', 'tests/test_people_mutation_idempotency_postgres.sh', 'tests/test_job_analysis_snapshot_postgres.sh', - 'tests/test_candidate_application_postgres.sh', - 'tests/test_candidate_application_rls_postgres.sh', - 'tests/test_candidate_application_decision_boundary_postgres.sh', 'tests/test_candidate_withdrawal_postgres.sh', 'tests/test_candidate_withdrawal_security_postgres.sh', 'tests/validate_repository.py' diff --git a/tests/validate_repository.py b/tests/validate_repository.py index 8badc2301..f12c82f3b 100644 --- a/tests/validate_repository.py +++ b/tests/validate_repository.py @@ -57,8 +57,10 @@ "docs/adr/0013-governed-requisition-review-packet.md", "docs/adr/0014-job-analysis-snapshot-persistence.md", "docs/adr/0026-normalized-candidate-application.md", + "docs/adr/0027-governed-candidate-withdrawal.md", "docs/doctoring/REFERENCES.md", "docs/doctoring/candidate-application-references.md", + "docs/doctoring/candidate-withdrawal-references.md", "docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md", "docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md", "database/migrations/0001_foundation_schema.sql", @@ -77,7 +79,9 @@ "database/migrations/0014_candidate_application_core.sql", "database/migrations/0015_candidate_withdrawal_governance.sql", "docs/traceability/candidate-application-core.md", + "docs/traceability/candidate-withdrawal.md", ".github/workflows/candidate-application-quality.yml", + ".github/workflows/candidate-withdrawal-quality.yml", "packages/hris-kernel/src/orgmetra_hris_kernel/audit.py", "packages/hris-kernel/tests/test_audit_outbox.py", "schemas/openapi.yaml", @@ -102,9 +106,6 @@ "tests/test_criterion_observation_scope_postgres.sh", "tests/test_people_mutation_idempotency_postgres.sh", "tests/test_job_analysis_snapshot_postgres.sh", - "tests/test_candidate_application_postgres.sh", - "tests/test_candidate_application_rls_postgres.sh", - "tests/test_candidate_application_decision_boundary_postgres.sh", "tests/test_candidate_withdrawal_postgres.sh", "tests/test_candidate_withdrawal_security_postgres.sh", "tests/validate_repository.py", From 18bf097b2daefa61f30d127f513d32f1333360ca Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 17:42:15 +0900 Subject: [PATCH 14/18] test(withdrawal): remove stale fixture values --- manifest.json | 2 +- tests/test_candidate_withdrawal_postgres.sh | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index d073d6ab1..234704c64 100644 --- a/manifest.json +++ b/manifest.json @@ -1 +1 @@ -{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/normalized-candidate-application","files":[{"path":".github/workflows/candidate-application-quality.yml","sha256":"3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931","bytes":3467,"lines":81},{"path":".github/workflows/candidate-withdrawal-quality.yml","sha256":"e32de389a89388c33e0bea1d88b71470f4931ff28dafb646b5f9a907063e1f7a","bytes":2449,"lines":68},{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"414858744d8242130adae52fa273e9843362c81eb645baff0d8b26a3ae50ad84","bytes":18517,"lines":78},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad","bytes":13889,"lines":307},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6","bytes":14913,"lines":90},{"path":"docs/ERD.md","sha256":"f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246","bytes":8331,"lines":77},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/0026-normalized-candidate-application.md","sha256":"ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081","bytes":9945,"lines":73},{"path":"docs/adr/0027-governed-candidate-withdrawal.md","sha256":"48d7c949989b80646f690d4185150ad50e8c31a3c29761a6be95b09849ba9d72","bytes":7850,"lines":81},{"path":"docs/adr/README.md","sha256":"19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e","bytes":2310,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/doctoring/candidate-application-references.md","sha256":"202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c","bytes":2962,"lines":24},{"path":"docs/doctoring/candidate-withdrawal-references.md","sha256":"a49be5ae37725158d7da7a82f6cc60df122f2cb086e1234bcecd5c85a41242c2","bytes":3923,"lines":39},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"docs/traceability/candidate-application-core.md","sha256":"3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14","bytes":6877,"lines":35},{"path":"docs/traceability/candidate-withdrawal.md","sha256":"49d6f47ce8130cac94e67bb0d6e13dc772b07f0f5e4b8c4a6816aa001166d046","bytes":4552,"lines":31},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"bb09a70cc1950cf0862c63d9824548d5b871eb2daf3bf9d783816c95265d3cdd","bytes":29751,"lines":719},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4","bytes":16371,"lines":427},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416","bytes":3954,"lines":98},{"path":"tests/test_candidate_application_postgres.sh","sha256":"69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686","bytes":14311,"lines":236},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563","bytes":4637,"lines":118},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"72e1376964a2d056ffa95b8078acb98df5771777ebbf6ba050a1d44b96b2f840","bytes":7914,"lines":230},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"984d4c068f9b1b9c8394fc9d1e70e909446946699d689d9dcc1b97ada8bcee3c","bytes":29130,"lines":682}]} +{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/normalized-candidate-application","files":[{"path":".github/workflows/candidate-application-quality.yml","sha256":"3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931","bytes":3467,"lines":81},{"path":".github/workflows/candidate-withdrawal-quality.yml","sha256":"e32de389a89388c33e0bea1d88b71470f4931ff28dafb646b5f9a907063e1f7a","bytes":2449,"lines":68},{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"414858744d8242130adae52fa273e9843362c81eb645baff0d8b26a3ae50ad84","bytes":18517,"lines":78},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad","bytes":13889,"lines":307},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6","bytes":14913,"lines":90},{"path":"docs/ERD.md","sha256":"f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246","bytes":8331,"lines":77},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/0026-normalized-candidate-application.md","sha256":"ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081","bytes":9945,"lines":73},{"path":"docs/adr/0027-governed-candidate-withdrawal.md","sha256":"48d7c949989b80646f690d4185150ad50e8c31a3c29761a6be95b09849ba9d72","bytes":7850,"lines":81},{"path":"docs/adr/README.md","sha256":"19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e","bytes":2310,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/doctoring/candidate-application-references.md","sha256":"202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c","bytes":2962,"lines":24},{"path":"docs/doctoring/candidate-withdrawal-references.md","sha256":"a49be5ae37725158d7da7a82f6cc60df122f2cb086e1234bcecd5c85a41242c2","bytes":3923,"lines":39},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"docs/traceability/candidate-application-core.md","sha256":"3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14","bytes":6877,"lines":35},{"path":"docs/traceability/candidate-withdrawal.md","sha256":"49d6f47ce8130cac94e67bb0d6e13dc772b07f0f5e4b8c4a6816aa001166d046","bytes":4552,"lines":31},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"bb09a70cc1950cf0862c63d9824548d5b871eb2daf3bf9d783816c95265d3cdd","bytes":29751,"lines":719},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4","bytes":16371,"lines":427},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416","bytes":3954,"lines":98},{"path":"tests/test_candidate_application_postgres.sh","sha256":"69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686","bytes":14311,"lines":236},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563","bytes":4637,"lines":118},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"019e07ca343d90a35f5f879309c53a298d5415b96de01ccfff40586f580510e3","bytes":7818,"lines":228},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"984d4c068f9b1b9c8394fc9d1e70e909446946699d689d9dcc1b97ada8bcee3c","bytes":29130,"lines":682}]} diff --git a/tests/test_candidate_withdrawal_postgres.sh b/tests/test_candidate_withdrawal_postgres.sh index 72e3af7e0..ac4b9e4d5 100644 --- a/tests/test_candidate_withdrawal_postgres.sh +++ b/tests/test_candidate_withdrawal_postgres.sh @@ -41,7 +41,6 @@ INSERT INTO candidate_application_record ( '10000000-0000-7000-8000-000000000001', '10000000-0000-7000-8000-000000000051', '10000000-0000-7000-8000-000000000011', - '10000000-0000-7000-8000-000000000021', 'requisition:11111111-1111-4111-8111-111111111111', TIMESTAMPTZ '2026-08-21 09:10:00+00', TIMESTAMPTZ '2026-08-21 09:10:01+00' @@ -50,7 +49,6 @@ INSERT INTO candidate_application_record ( '20000000-0000-7000-8000-000000000001', '20000000-0000-7000-8000-000000000051', '20000000-0000-7000-8000-000000000011', - '20000000-0000-7000-8000-000000000021', 'requisition:22222222-2222-4222-8222-222222222222', TIMESTAMPTZ '2026-08-21 09:10:00+00', TIMESTAMPTZ '2026-08-21 09:10:01+00' From f0afa82ec4d0bfa6404b76d1bb7817e96a93b75d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 08:30:15 +0900 Subject: [PATCH 15/18] fix(candidate-withdrawal): migrate audit envelope validator forward --- .../candidate-withdrawal-quality.yml | 38 ++ CHANGELOG.md | 1 + .../0008_audit_outbox_review_hardening.sql | 49 +- ...16_candidate_withdrawal_audit_envelope.sql | 243 ++++++++ docs/traceability/candidate-withdrawal.md | 1 + manifest.json | 584 +++++++++++++++++- scripts/foundation-contract-core.mjs | 2 + ...e_withdrawal_migration_upgrade_postgres.sh | 70 +++ tests/validate_repository.py | 2 + 9 files changed, 941 insertions(+), 49 deletions(-) create mode 100644 database/migrations/0016_candidate_withdrawal_audit_envelope.sql create mode 100644 tests/test_candidate_withdrawal_migration_upgrade_postgres.sh diff --git a/.github/workflows/candidate-withdrawal-quality.yml b/.github/workflows/candidate-withdrawal-quality.yml index 3d3ed074b..b1f6ef160 100644 --- a/.github/workflows/candidate-withdrawal-quality.yml +++ b/.github/workflows/candidate-withdrawal-quality.yml @@ -58,6 +58,7 @@ jobs: bash -n tests/test_candidate_withdrawal_postgres.sh tests/test_candidate_withdrawal_security_postgres.sh + tests/test_candidate_withdrawal_migration_upgrade_postgres.sh - name: Prove governed candidate withdrawal persistence run: bash tests/test_candidate_withdrawal_postgres.sh - name: Prove anti-forgery and tenant isolation @@ -66,3 +67,40 @@ jobs: run: | git diff --exit-code test -z "$(git status --porcelain)" + + postgres_migration_upgrade_contract: + name: Candidate withdrawal migration upgrade 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: Prove migration upgrade path + run: bash tests/test_candidate_withdrawal_migration_upgrade_postgres.sh + - name: Prove validation is read-only + run: | + git diff --exit-code + test -z "$(git status --porcelain)" diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ce9f8399..8da8eb2f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to Orgmetra will be documented in this file. - Accepted ADRs 0001–0003 now include buyer-facing Context, Decision, and Consequences grounded in verified ISO 30400:2022, ISO 30414:2025, Uniform Guidelines (29 C.F.R. Part 1607), SIOP (2018), OpenAPI Specification v3.2.0, OpenID Connect Core 1.0 errata set 2, CloudEvents v1.0.2, Jensen and Snodgrass (1999), Snodgrass (1999), and Allen (1983) records already listed in `docs/doctoring/REFERENCES.md`. ADRs 0004 and 0005 gained APA 7th References pointers to that same bibliography without changing their Decision bodies. - Active-PR governed Job Analysis persistence/API on the canonical `JobAnalysisSnapshot` model: migration `0013_job_analysis_snapshot.sql` stores immutable tenant-scoped snapshot, Task, KSAO, Task–KSAO, FJA and write-command evidence; `POST /v1/tenants/{tenant_record_id}/job-analysis-snapshots` and matching GET enforce purpose-bound Keyverse scope, authenticated-principal actor authority, bounded/strict JSON handling, transactional Idempotency-Key serialization, parent-scope fail-closed integrity, forced RLS, and atomic audit/outbox evidence. ADR 0014 records the persistence decision while ADR 0007 remains the domain/evidence authority; validated evidence still requires accountable human review and non-LLM provenance, and the service does not make a high-impact employment decision. - Active-PR candidate-withdrawal persistence now binds identity-resolution reference/digest, withdrawal evidence reference/digest, and evidence version into the candidate-specific immutable audit envelope as well as the append-only withdrawal fact; generic audit envelopes retain their exact existing data contract. +- Active-PR candidate-withdrawal persistence now delivers its audit-envelope extension through forward-only migration `0016_candidate_withdrawal_audit_envelope.sql`, preserving already-applied migration `0008` while proving the legacy-to-upgraded validator path in a disposable PostgreSQL contract. - Active-PR `orgmetra_selection_review` packet for PII-minimized, evidence-bound human selection review: canonical operational tenant identity, UUID-backed opaque candidate/Job/sealed-evidence/reviewer references, explicit purpose/reason/evidence version, deterministic canonical JSON and SHA-256 correlation, mandatory human decision state, redacted packet repr, and provenance-paired model evidence that remains `untrusted_draft`, with exact 100% owned statement and branch coverage required by its quality gate. - Active performance-criterion scope hardening: `criterion_observation_scope_guard` rejects criterion outcomes for a Job the worker did not effectively hold at the observation date, observations before the relevant assignment, and observations outside the referenced performance cycle while preserving valid multiple-assignment cases and existing bitemporal correction semantics. The guard evaluates current-recorded facts, derives the date coordinate from `observed_at` in UTC so session `TimeZone` cannot alter the result, uses a trusted function search path, and adds no PII or automated employment decision authority. The Foundation PostgreSQL contract also rejects a closed `recorded_to` on each time-coordinate lookup and proves UTC midnight plus non-UTC session `TimeZone` boundaries. - Bitemporal tenant-scoped organization hierarchy validation that rejects visible indirect parent cycles and reuses single-valued recorded-time reconstruction before graph traversal. diff --git a/database/migrations/0008_audit_outbox_review_hardening.sql b/database/migrations/0008_audit_outbox_review_hardening.sql index 077a3630d..0aeec1f62 100644 --- a/database/migrations/0008_audit_outbox_review_hardening.sql +++ b/database/migrations/0008_audit_outbox_review_hardening.sql @@ -88,14 +88,6 @@ DECLARE 'time', 'type' ]; - candidate_withdrawal_data_keys constant text[] := ARRAY[ - 'evidence_version', - 'high_impact', - 'identity_resolution_digest', - 'identity_resolution_reference', - 'result_code', - 'withdrawal_evidence_digest' - ]; BEGIN IF public.is_operational_uuid(p_audit_event_record_id) IS NOT TRUE OR public.is_operational_uuid(p_tenant_record_id) IS NOT TRUE THEN @@ -133,12 +125,7 @@ BEGIN SELECT pg_catalog.array_agg(data_key ORDER BY data_key COLLATE "C") INTO data_keys FROM pg_catalog.jsonb_object_keys(event_data) AS data_key_set(data_key); - IF event_envelope ->> 'source' = 'urn:orgmetra:talent_acquisition' - AND event_envelope ->> 'type' = 'orgmetra.candidate.application_withdrawn' THEN - IF data_keys IS DISTINCT FROM candidate_withdrawal_data_keys THEN - RETURN false; - END IF; - ELSIF data_keys IS DISTINCT FROM ARRAY['high_impact', 'result_code']::text[] THEN + IF data_keys IS DISTINCT FROM ARRAY['high_impact', 'result_code']::text[] THEN RETURN false; END IF; @@ -182,40 +169,6 @@ BEGIN RETURN false; END IF; - IF event_envelope ->> 'source' = 'urn:orgmetra:talent_acquisition' - AND event_envelope ->> 'type' = 'orgmetra.candidate.application_withdrawn' - AND ( - pg_catalog.jsonb_typeof(event_data -> 'evidence_version') - IS DISTINCT FROM 'number' - OR (event_data ->> 'evidence_version') IS NULL - OR ( - CASE - WHEN (event_data ->> 'evidence_version') COLLATE "C" - ~ '^[1-9][0-9]{0,6}$' - THEN (event_data ->> 'evidence_version')::integer - BETWEEN 1 AND 1000000 - ELSE false - END - ) IS NOT TRUE - OR pg_catalog.jsonb_typeof(event_data -> 'identity_resolution_reference') - IS DISTINCT FROM 'string' - OR (event_data ->> 'identity_resolution_reference') IS NULL - OR (event_data ->> 'identity_resolution_reference') COLLATE "C" - !~ '^identity_resolution:[A-Za-z0-9][A-Za-z0-9._~-]*$' - OR pg_catalog.jsonb_typeof(event_data -> 'identity_resolution_digest') - IS DISTINCT FROM 'string' - OR (event_data ->> 'identity_resolution_digest') IS NULL - OR (event_data ->> 'identity_resolution_digest') COLLATE "C" - !~ '^[0-9a-f]{64}$' - OR pg_catalog.jsonb_typeof(event_data -> 'withdrawal_evidence_digest') - IS DISTINCT FROM 'string' - OR (event_data ->> 'withdrawal_evidence_digest') IS NULL - OR (event_data ->> 'withdrawal_evidence_digest') COLLATE "C" - !~ '^[0-9a-f]{64}$' - ) THEN - RETURN false; - END IF; - event_time_text := event_envelope ->> 'time'; IF event_time_text COLLATE "C" !~ '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$' THEN diff --git a/database/migrations/0016_candidate_withdrawal_audit_envelope.sql b/database/migrations/0016_candidate_withdrawal_audit_envelope.sql new file mode 100644 index 000000000..fc2cab2c7 --- /dev/null +++ b/database/migrations/0016_candidate_withdrawal_audit_envelope.sql @@ -0,0 +1,243 @@ +-- Extend the already-deployed audit envelope validator through a forward-only +-- migration. Do not edit 0008: existing databases must receive this change. + +BEGIN; + +SET LOCAL search_path = public, pg_catalog; + +CREATE OR REPLACE FUNCTION public.validate_audit_event_envelope( + p_canonical_event_json text, + p_audit_event_record_id uuid, + p_tenant_record_id uuid, + p_event_envelope_digest text +) +RETURNS boolean +LANGUAGE plpgsql +IMMUTABLE +STRICT +SET search_path = pg_catalog, public, pg_temp +AS $$ +DECLARE + event_envelope jsonb; + event_data jsonb; + event_keys text[]; + data_keys text[]; + event_high_impact boolean; + event_time_text text; + event_year integer; + event_month integer; + event_day integer; + event_hour integer; + event_minute integer; + event_second integer; + expected_keys_without_confirmation constant text[] := ARRAY[ + 'data', + 'datacontenttype', + 'id', + 'orgmetraactor', + 'orgmetraevidence', + 'orgmetrapurpose', + 'orgmetrareason', + 'orgmetratenant', + 'source', + 'specversion', + 'subject', + 'time', + 'type' + ]; + expected_keys_with_confirmation constant text[] := ARRAY[ + 'data', + 'datacontenttype', + 'id', + 'orgmetraactor', + 'orgmetraconfirmation', + 'orgmetraevidence', + 'orgmetrapurpose', + 'orgmetrareason', + 'orgmetratenant', + 'source', + 'specversion', + 'subject', + 'time', + 'type' + ]; + candidate_withdrawal_data_keys constant text[] := ARRAY[ + 'evidence_version', + 'high_impact', + 'identity_resolution_digest', + 'identity_resolution_reference', + 'result_code', + 'withdrawal_evidence_digest' + ]; +BEGIN + IF public.is_operational_uuid(p_audit_event_record_id) IS NOT TRUE + OR public.is_operational_uuid(p_tenant_record_id) IS NOT TRUE THEN + RETURN false; + END IF; + + BEGIN + event_envelope := p_canonical_event_json::jsonb; + EXCEPTION + WHEN others THEN + RETURN false; + END; + + IF pg_catalog.jsonb_typeof(event_envelope) <> 'object' THEN + RETURN false; + END IF; + + SELECT pg_catalog.array_agg(event_key ORDER BY event_key COLLATE "C") + INTO event_keys + FROM pg_catalog.jsonb_object_keys(event_envelope) AS event_key_set(event_key); + + IF event_keys IS NULL + OR ( + event_keys IS DISTINCT FROM expected_keys_without_confirmation + AND event_keys IS DISTINCT FROM expected_keys_with_confirmation + ) THEN + RETURN false; + END IF; + + event_data := event_envelope -> 'data'; + IF pg_catalog.jsonb_typeof(event_data) <> 'object' THEN + RETURN false; + END IF; + + SELECT pg_catalog.array_agg(data_key ORDER BY data_key COLLATE "C") + INTO data_keys + FROM pg_catalog.jsonb_object_keys(event_data) AS data_key_set(data_key); + IF event_envelope ->> 'source' = 'urn:orgmetra:talent_acquisition' + AND event_envelope ->> 'type' = 'orgmetra.candidate.application_withdrawn' THEN + IF data_keys IS DISTINCT FROM candidate_withdrawal_data_keys THEN + RETURN false; + END IF; + ELSIF data_keys IS DISTINCT FROM ARRAY['high_impact', 'result_code']::text[] THEN + RETURN false; + END IF; + + IF event_envelope ->> 'specversion' <> '1.0' + OR event_envelope ->> 'datacontenttype' <> 'application/json' + OR event_envelope ->> 'id' <> p_audit_event_record_id::text + OR event_envelope ->> 'orgmetratenant' <> p_tenant_record_id::text THEN + RETURN false; + END IF; + + IF pg_catalog.jsonb_typeof(event_envelope -> 'id') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'source') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'type') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'subject') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'time') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'orgmetraactor') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'orgmetrapurpose') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'orgmetrareason') <> 'string' + OR pg_catalog.jsonb_typeof(event_envelope -> 'orgmetraevidence') <> 'string' + OR pg_catalog.jsonb_typeof(event_data -> 'result_code') <> 'string' + OR pg_catalog.jsonb_typeof(event_data -> 'high_impact') <> 'boolean' THEN + RETURN false; + END IF; + + IF (event_envelope ->> 'source') COLLATE "C" + !~ '^urn:orgmetra:[a-z][a-z0-9]*(?:_[a-z0-9]+)+$' + OR (event_envelope ->> 'type') COLLATE "C" + !~ '^orgmetra(?:\.[a-z][a-z0-9_]*){2,}$' + OR (event_envelope ->> 'subject') COLLATE "C" + !~ '^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$' + OR (event_envelope ->> 'orgmetraactor') COLLATE "C" + !~ '^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$' + OR (event_envelope ->> 'orgmetrapurpose') COLLATE "C" + !~ '^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$' + OR (event_envelope ->> 'orgmetrareason') COLLATE "C" + !~ '^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$' + OR (event_envelope ->> 'orgmetraevidence') COLLATE "C" + !~ '^[A-Za-z0-9][A-Za-z0-9._:-]*$' + OR (event_data ->> 'result_code') COLLATE "C" + !~ '^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$' THEN + RETURN false; + END IF; + + IF event_envelope ->> 'source' = 'urn:orgmetra:talent_acquisition' + AND event_envelope ->> 'type' = 'orgmetra.candidate.application_withdrawn' + AND ( + pg_catalog.jsonb_typeof(event_data -> 'evidence_version') + IS DISTINCT FROM 'number' + OR (event_data ->> 'evidence_version') IS NULL + OR ( + CASE + WHEN (event_data ->> 'evidence_version') COLLATE "C" + ~ '^[1-9][0-9]{0,6}$' + THEN (event_data ->> 'evidence_version')::integer + BETWEEN 1 AND 1000000 + ELSE false + END + ) IS NOT TRUE + OR pg_catalog.jsonb_typeof(event_data -> 'identity_resolution_reference') + IS DISTINCT FROM 'string' + OR (event_data ->> 'identity_resolution_reference') IS NULL + OR (event_data ->> 'identity_resolution_reference') COLLATE "C" + !~ '^identity_resolution:[A-Za-z0-9][A-Za-z0-9._~-]*$' + OR pg_catalog.jsonb_typeof(event_data -> 'identity_resolution_digest') + IS DISTINCT FROM 'string' + OR (event_data ->> 'identity_resolution_digest') IS NULL + OR (event_data ->> 'identity_resolution_digest') COLLATE "C" + !~ '^[0-9a-f]{64}$' + OR pg_catalog.jsonb_typeof(event_data -> 'withdrawal_evidence_digest') + IS DISTINCT FROM 'string' + OR (event_data ->> 'withdrawal_evidence_digest') IS NULL + OR (event_data ->> 'withdrawal_evidence_digest') COLLATE "C" + !~ '^[0-9a-f]{64}$' + ) THEN + RETURN false; + END IF; + + event_time_text := event_envelope ->> 'time'; + IF event_time_text COLLATE "C" + !~ '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$' THEN + RETURN false; + END IF; + + BEGIN + event_year := pg_catalog.substr(event_time_text, 1, 4)::integer; + event_month := pg_catalog.substr(event_time_text, 6, 2)::integer; + event_day := pg_catalog.substr(event_time_text, 9, 2)::integer; + event_hour := pg_catalog.substr(event_time_text, 12, 2)::integer; + event_minute := pg_catalog.substr(event_time_text, 15, 2)::integer; + event_second := pg_catalog.substr(event_time_text, 18, 2)::integer; + PERFORM pg_catalog.make_date(event_year, event_month, event_day); + EXCEPTION + WHEN others THEN + RETURN false; + END; + + IF event_hour > 23 OR event_minute > 59 OR event_second > 59 THEN + RETURN false; + END IF; + + event_high_impact := (event_data ->> 'high_impact')::boolean; + IF event_high_impact THEN + IF NOT (event_envelope ? 'orgmetraconfirmation') + OR pg_catalog.jsonb_typeof(event_envelope -> 'orgmetraconfirmation') <> 'string' + OR (event_envelope ->> 'orgmetraconfirmation') COLLATE "C" + !~ '^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$' THEN + RETURN false; + END IF; + ELSIF event_envelope ? 'orgmetraconfirmation' THEN + IF pg_catalog.jsonb_typeof(event_envelope -> 'orgmetraconfirmation') <> 'string' + OR (event_envelope ->> 'orgmetraconfirmation') COLLATE "C" + !~ '^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$' THEN + RETURN false; + END IF; + END IF; + + IF p_event_envelope_digest COLLATE "C" !~ '^[0-9a-f]{64}$' + OR pg_catalog.encode( + public.digest(pg_catalog.convert_to(p_canonical_event_json, 'UTF8'), 'sha256'), + 'hex' + ) <> p_event_envelope_digest THEN + RETURN false; + END IF; + + RETURN true; +END; +$$; + +COMMIT; diff --git a/docs/traceability/candidate-withdrawal.md b/docs/traceability/candidate-withdrawal.md index 8f16890bb..0dbc1e3e5 100644 --- a/docs/traceability/candidate-withdrawal.md +++ b/docs/traceability/candidate-withdrawal.md @@ -13,6 +13,7 @@ | Identity syntax is not treated as authentication | row requires `identity_resolution_reference` and SHA-256 digest; authorized identity adapter remains responsible for claimant→candidate resolution | ADR 0027 documents the adapter boundary; database tests prove the evidence shape is mandatory but do **not** claim external identity authentication | | Withdrawal evidence is versioned and immutable | withdrawal evidence reference/digest, bounded `evidence_version`, append-only UPDATE/DELETE/TRUNCATE guards | both candidate-withdrawal PostgreSQL tests | | Audit envelope binds the exact withdrawal | insert trigger requires exact subject, actor, identity-resolution reference/digest, evidence reference/digest, evidence version, purpose, reason, event time and result; transactional outbox row required | `test_candidate_withdrawal_security_postgres.sh` supplies candidate-shaped but mismatched provenance and proves fail-closed rejection | +| Existing databases receive the withdrawal envelope rule | forward-only `0016_candidate_withdrawal_audit_envelope.sql` replaces the already-deployed validator after legacy `0008` has run; `0008` itself remains immutable | `test_candidate_withdrawal_migration_upgrade_postgres.sh` proves legacy rejection followed by upgraded acceptance | | Candidate withdrawal is not an employer high-impact decision | audit envelope requires `high_impact=false` and has no `orgmetraconfirmation`; employer outcomes remain in `selection_decision` | `test_candidate_withdrawal_postgres.sh`; migration trigger predicates | | Raw ATS workflow cannot smuggle terminal withdrawal | PR #66 stage vocabulary remains non-terminal; PR #67 does not add `withdrawn` to it | `test_candidate_withdrawal_postgres.sh` attempts raw `withdrawn` stage insertion and requires `candidate_application_stage_code_check` | | Application chronology is preserved | withdrawal cannot predate application `submitted_at`; audit time must equal `withdrawn_at` and not exceed `recorded_at` | migration trigger exercised by happy-path and anti-forgery contracts | diff --git a/manifest.json b/manifest.json index 234704c64..16f93801f 100644 --- a/manifest.json +++ b/manifest.json @@ -1 +1,583 @@ -{"package":"orgmetra-foundation-pack","version":"0.1.0","generated_for_branch":"feat/normalized-candidate-application","files":[{"path":".github/workflows/candidate-application-quality.yml","sha256":"3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931","bytes":3467,"lines":81},{"path":".github/workflows/candidate-withdrawal-quality.yml","sha256":"e32de389a89388c33e0bea1d88b71470f4931ff28dafb646b5f9a907063e1f7a","bytes":2449,"lines":68},{"path":".github/workflows/foundation-ci.yml","sha256":"12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537","bytes":4379,"lines":123},{"path":".github/workflows/job-analysis-api-quality.yml","sha256":"352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a","bytes":4159,"lines":105},{"path":".gitignore","sha256":"145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21","bytes":375,"lines":37},{"path":"AGENTS.md","sha256":"28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16","bytes":2246,"lines":34},{"path":"ARCHITECTURE.md","sha256":"52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850","bytes":7864,"lines":107},{"path":"CHANGELOG.md","sha256":"414858744d8242130adae52fa273e9843362c81eb645baff0d8b26a3ae50ad84","bytes":18517,"lines":78},{"path":"CLAUDE.md","sha256":"add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f","bytes":1229,"lines":20},{"path":"LICENSE","sha256":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","bytes":11358,"lines":202},{"path":"NOTICE","sha256":"34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042","bytes":305,"lines":4},{"path":"README.md","sha256":"1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6","bytes":3785,"lines":81},{"path":"database/migrations/0001_foundation_schema.sql","sha256":"ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd","bytes":38747,"lines":916},{"path":"database/migrations/0002_sealed_evidence_digest.sql","sha256":"93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c","bytes":6649,"lines":202},{"path":"database/migrations/0003_audit_outbox_persistence.sql","sha256":"2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc","bytes":15417,"lines":423},{"path":"database/migrations/0004_outbox_delivery_claim.sql","sha256":"d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef","bytes":9451,"lines":234},{"path":"database/migrations/0005_outbox_delivery_finalization.sql","sha256":"b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961","bytes":6125,"lines":170},{"path":"database/migrations/0006_outbox_delivery_dead_letter.sql","sha256":"c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7","bytes":24919,"lines":628},{"path":"database/migrations/0007_outbox_retry_exhaustion.sql","sha256":"812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5","bytes":19081,"lines":476},{"path":"database/migrations/0008_audit_outbox_review_hardening.sql","sha256":"9eee390396c29394419b717c4b95e73048fbf817a3200cd60a343d02013f626e","bytes":19769,"lines":495},{"path":"database/migrations/0009_candidate_worker_conversion_governance.sql","sha256":"4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9","bytes":11537,"lines":281},{"path":"database/migrations/0010_validity_study_case_integrity.sql","sha256":"3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1","bytes":11979,"lines":313},{"path":"database/migrations/0011_criterion_observation_scope.sql","sha256":"f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9","bytes":7444,"lines":165},{"path":"database/migrations/0012_people_mutation_idempotency.sql","sha256":"52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69","bytes":3162,"lines":76},{"path":"database/migrations/0013_job_analysis_snapshot.sql","sha256":"b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee","bytes":12713,"lines":260},{"path":"database/migrations/0014_candidate_application_core.sql","sha256":"0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad","bytes":13889,"lines":307},{"path":"database/migrations/0015_candidate_withdrawal_governance.sql","sha256":"54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320","bytes":8236,"lines":192},{"path":"docs/API_CONTRACT.md","sha256":"63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589","bytes":4555,"lines":76},{"path":"docs/DATA_MODEL.md","sha256":"9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6","bytes":14913,"lines":90},{"path":"docs/ERD.md","sha256":"f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246","bytes":8331,"lines":77},{"path":"docs/OPERABILITY.md","sha256":"82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62","bytes":11189,"lines":71},{"path":"docs/PRD.md","sha256":"3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1","bytes":5490,"lines":111},{"path":"docs/SECURITY.md","sha256":"01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac","bytes":11185,"lines":64},{"path":"docs/STORYBOARD.md","sha256":"6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2","bytes":1342,"lines":28},{"path":"docs/STORYBOOK.md","sha256":"82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9","bytes":1389,"lines":50},{"path":"docs/TEST_STRATEGY.md","sha256":"d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8","bytes":16534,"lines":135},{"path":"docs/THREAT_MODEL.md","sha256":"f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252","bytes":6736,"lines":23},{"path":"docs/TRACEABILITY.md","sha256":"dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e","bytes":11462,"lines":40},{"path":"docs/TRD.md","sha256":"23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077","bytes":9064,"lines":101},{"path":"docs/UML.md","sha256":"fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9","bytes":5528,"lines":122},{"path":"docs/USER_STORIES.md","sha256":"5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f","bytes":2670,"lines":37},{"path":"docs/WIREFRAMES.md","sha256":"b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e","bytes":2005,"lines":77},{"path":"docs/adr/0001-orgmetra-authoritative-hris-record.md","sha256":"0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572","bytes":6108,"lines":53},{"path":"docs/adr/0002-federated-cwl-integration-boundaries.md","sha256":"b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2","bytes":4072,"lines":44},{"path":"docs/adr/0003-bitemporal-hris-data-contract.md","sha256":"d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799","bytes":4453,"lines":47},{"path":"docs/adr/0004-employment-position-version-and-assignment-binding.md","sha256":"fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182","bytes":1872,"lines":30},{"path":"docs/adr/0005-exclusive-employment-and-staffable-seats.md","sha256":"10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b","bytes":2091,"lines":34},{"path":"docs/adr/0006-governed-audit-outbox-envelope.md","sha256":"827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd","bytes":14100,"lines":66},{"path":"docs/adr/0007-governed-job-analysis-evidence.md","sha256":"953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52","bytes":5653,"lines":57},{"path":"docs/adr/0008-purpose-bound-pii-authorization.md","sha256":"c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7","bytes":5988,"lines":55},{"path":"docs/adr/0009-performance-criterion-observation-scope.md","sha256":"1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64","bytes":7057,"lines":57},{"path":"docs/adr/0010-naruon-calendar-intent-boundary.md","sha256":"3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9","bytes":3917,"lines":35},{"path":"docs/adr/0011-bitemporal-workforce-composition.md","sha256":"1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b","bytes":5568,"lines":53},{"path":"docs/adr/0012-governed-migration-handoff.md","sha256":"713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80","bytes":5965,"lines":59},{"path":"docs/adr/0013-governed-requisition-review-packet.md","sha256":"70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802","bytes":4693,"lines":46},{"path":"docs/adr/0014-job-analysis-snapshot-persistence.md","sha256":"a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105","bytes":5365,"lines":49},{"path":"docs/adr/0026-normalized-candidate-application.md","sha256":"ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081","bytes":9945,"lines":73},{"path":"docs/adr/0027-governed-candidate-withdrawal.md","sha256":"48d7c949989b80646f690d4185150ad50e8c31a3c29761a6be95b09849ba9d72","bytes":7850,"lines":81},{"path":"docs/adr/README.md","sha256":"19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e","bytes":2310,"lines":22},{"path":"docs/doctoring/REFERENCES.md","sha256":"929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5","bytes":6352,"lines":69},{"path":"docs/doctoring/candidate-application-references.md","sha256":"202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c","bytes":2962,"lines":24},{"path":"docs/doctoring/candidate-withdrawal-references.md","sha256":"a49be5ae37725158d7da7a82f6cc60df122f2cb086e1234bcecd5c85a41242c2","bytes":3923,"lines":39},{"path":"docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md","sha256":"b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd","bytes":8227,"lines":226},{"path":"docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md","sha256":"4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d","bytes":6237,"lines":187},{"path":"docs/traceability/candidate-application-core.md","sha256":"3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14","bytes":6877,"lines":35},{"path":"docs/traceability/candidate-withdrawal.md","sha256":"49d6f47ce8130cac94e67bb0d6e13dc772b07f0f5e4b8c4a6816aa001166d046","bytes":4552,"lines":31},{"path":"package.json","sha256":"59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5","bytes":388,"lines":9},{"path":"packages/hris-kernel/src/orgmetra_hris_kernel/audit.py","sha256":"3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190","bytes":7707,"lines":160},{"path":"packages/hris-kernel/tests/test_audit_outbox.py","sha256":"5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c","bytes":7556,"lines":200},{"path":"schemas/openapi.yaml","sha256":"09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f","bytes":29503,"lines":1020},{"path":"scripts/foundation-contract-core.mjs","sha256":"bb09a70cc1950cf0862c63d9824548d5b871eb2daf3bf9d783816c95265d3cdd","bytes":29751,"lines":719},{"path":"scripts/foundation-contract.mjs","sha256":"5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a","bytes":218,"lines":6},{"path":"tests/dispatcher-inventory.test.mjs","sha256":"09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261","bytes":1597,"lines":34},{"path":"tests/foundation-contract.test.mjs","sha256":"3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4","bytes":16371,"lines":427},{"path":"tests/openapi-contract.test.mjs","sha256":"80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc","bytes":6438,"lines":195},{"path":"tests/test_audit_outbox_hardening_postgres.sh","sha256":"518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0","bytes":13396,"lines":333},{"path":"tests/test_audit_outbox_postgres.sh","sha256":"e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2","bytes":13443,"lines":357},{"path":"tests/test_bitemporal_postgres.sh","sha256":"7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc","bytes":8209,"lines":230},{"path":"tests/test_candidate_application_decision_boundary_postgres.sh","sha256":"887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416","bytes":3954,"lines":98},{"path":"tests/test_candidate_application_postgres.sh","sha256":"69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686","bytes":14311,"lines":236},{"path":"tests/test_candidate_application_rls_postgres.sh","sha256":"0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563","bytes":4637,"lines":118},{"path":"tests/test_candidate_withdrawal_postgres.sh","sha256":"019e07ca343d90a35f5f879309c53a298d5415b96de01ccfff40586f580510e3","bytes":7818,"lines":228},{"path":"tests/test_candidate_withdrawal_security_postgres.sh","sha256":"0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7","bytes":12375,"lines":317},{"path":"tests/test_candidate_worker_conversion_postgres.sh","sha256":"681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90","bytes":14673,"lines":344},{"path":"tests/test_criterion_observation_scope_postgres.sh","sha256":"0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d","bytes":17811,"lines":469},{"path":"tests/test_evidence_sealing_postgres.sh","sha256":"57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7","bytes":11349,"lines":370},{"path":"tests/test_job_analysis_snapshot_postgres.sh","sha256":"ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f","bytes":13542,"lines":296},{"path":"tests/test_operational_uuid_postgres.sh","sha256":"7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7","bytes":3346,"lines":101},{"path":"tests/test_outbox_claim_postgres.sh","sha256":"1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b","bytes":14817,"lines":429},{"path":"tests/test_outbox_dead_letter_postgres.sh","sha256":"0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d","bytes":14008,"lines":377},{"path":"tests/test_people_mutation_idempotency_postgres.sh","sha256":"3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5","bytes":16191,"lines":381},{"path":"tests/test_tenant_isolation_postgres.sh","sha256":"dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a","bytes":15134,"lines":388},{"path":"tests/test_validity_study_case_postgres.sh","sha256":"0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02","bytes":14708,"lines":301},{"path":"tests/validate_repository.py","sha256":"984d4c068f9b1b9c8394fc9d1e70e909446946699d689d9dcc1b97ada8bcee3c","bytes":29130,"lines":682}]} +{ + "package": "orgmetra-foundation-pack", + "version": "0.1.0", + "generated_for_branch": "feat/normalized-candidate-application", + "files": [ + { + "path": ".github/workflows/candidate-application-quality.yml", + "sha256": "3386ec450a411a57a4e3c3bfec1558287cd04a229131f39873cc49f3e301c931", + "bytes": 3467, + "lines": 81 + }, + { + "path": ".github/workflows/candidate-withdrawal-quality.yml", + "sha256": "817757efe58a1ee94662d2537dfa9fa6f28ae27499eb36209d664bb5174e1544", + "bytes": 3949, + "lines": 106 + }, + { + "path": ".github/workflows/foundation-ci.yml", + "sha256": "12686a3bbd6445e6fdb202b4137dae118ddeeab1efb0c7f18ea6c8fa19d62537", + "bytes": 4379, + "lines": 123 + }, + { + "path": ".github/workflows/job-analysis-api-quality.yml", + "sha256": "352dc78931dd94afea3e88912d38dcc4b562a004112f199f3d7a12d22b6d637a", + "bytes": 4159, + "lines": 105 + }, + { + "path": ".gitignore", + "sha256": "145fda644f5209fa1fb3e3b40c9af9258bfac6d1a634bba2520fd08fe6d77a21", + "bytes": 375, + "lines": 37 + }, + { + "path": "AGENTS.md", + "sha256": "28f7b7bc010a7739cfdc3e793fb5d39a0e74b842ea9c190e9a251e2d0cbc3a16", + "bytes": 2246, + "lines": 34 + }, + { + "path": "ARCHITECTURE.md", + "sha256": "52d68786f7359c1a50d804996021e4c70e90accd2fff6f1a27c91de1dd8df850", + "bytes": 7864, + "lines": 107 + }, + { + "path": "CHANGELOG.md", + "sha256": "18f999729647bad231b926874af9d85c75cd9ea60a43993fcce11de1baa794bf", + "bytes": 18816, + "lines": 79 + }, + { + "path": "CLAUDE.md", + "sha256": "add33884f466d324e20875388d103de41c6e062938a6e98727dc83a87ffe976f", + "bytes": 1229, + "lines": 20 + }, + { + "path": "LICENSE", + "sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "bytes": 11358, + "lines": 202 + }, + { + "path": "NOTICE", + "sha256": "34b4618e946bdd8d33407d6ac5279f0a0388f5e7c8f79d2e7d8c3c47d0266042", + "bytes": 305, + "lines": 4 + }, + { + "path": "README.md", + "sha256": "1a9fc400d26d8137ae5911488794a6d3fa915957c95f27b36a48cef0fdf823c6", + "bytes": 3785, + "lines": 81 + }, + { + "path": "database/migrations/0001_foundation_schema.sql", + "sha256": "ce2ae52fc66b2f99597ea5285df82c66f90caa46174fef4930d68a8b6177d0dd", + "bytes": 38747, + "lines": 916 + }, + { + "path": "database/migrations/0002_sealed_evidence_digest.sql", + "sha256": "93d659ca8e0e9293a83d5422d043be7b1022c5470a5b22670aa3416fa334a04c", + "bytes": 6649, + "lines": 202 + }, + { + "path": "database/migrations/0003_audit_outbox_persistence.sql", + "sha256": "2aa7bbb8220923ec584537c0cd46f0cba2b692d69d431f097b7df6db75235bfc", + "bytes": 15417, + "lines": 423 + }, + { + "path": "database/migrations/0004_outbox_delivery_claim.sql", + "sha256": "d4504acf7d58528a2a8f4f03d1584b868c8d3ba9046a007b9c2e7cfef993b2ef", + "bytes": 9451, + "lines": 234 + }, + { + "path": "database/migrations/0005_outbox_delivery_finalization.sql", + "sha256": "b7e8790595b288f752d6ef5cc6cbfe4e1b6712248f5b7a3a25fa60016b6a4961", + "bytes": 6125, + "lines": 170 + }, + { + "path": "database/migrations/0006_outbox_delivery_dead_letter.sql", + "sha256": "c1fb91cdf98169fd6684984e86cb0a14fa19c8f1226028d2346a2a069df2b3c7", + "bytes": 24919, + "lines": 628 + }, + { + "path": "database/migrations/0007_outbox_retry_exhaustion.sql", + "sha256": "812f50d70ca5929c7eba964d34a208aedee660d11cc7ffc09d67688c4737e0d5", + "bytes": 19081, + "lines": 476 + }, + { + "path": "database/migrations/0008_audit_outbox_review_hardening.sql", + "sha256": "c3713a12db9d00fdc10005df1f86c07965e9555eefad78ca67e994537a739d9b", + "bytes": 17562, + "lines": 448 + }, + { + "path": "database/migrations/0009_candidate_worker_conversion_governance.sql", + "sha256": "4030666629a6b8deb383b8337ead4f09d6a945969313def2577a38f31f06cda9", + "bytes": 11537, + "lines": 281 + }, + { + "path": "database/migrations/0010_validity_study_case_integrity.sql", + "sha256": "3f594810ac9e1a6747a2bb4838e5ce65b921cb6e3d36fcdc3ff08b4a7579ebd1", + "bytes": 11979, + "lines": 313 + }, + { + "path": "database/migrations/0011_criterion_observation_scope.sql", + "sha256": "f9fe7c35f1ee7b167e1c2ba75a50a84febda9a6ccf8123b4f5726f51968694f9", + "bytes": 7444, + "lines": 165 + }, + { + "path": "database/migrations/0012_people_mutation_idempotency.sql", + "sha256": "52dbbb9ec7f9be5291593ba88f228d7fffd736dcb99547a08c1d6cad076afb69", + "bytes": 3162, + "lines": 76 + }, + { + "path": "database/migrations/0013_job_analysis_snapshot.sql", + "sha256": "b6553a5a4c94c4aa9f341a474e13bbe34db63044eda2446b3ebee178995977ee", + "bytes": 12713, + "lines": 260 + }, + { + "path": "database/migrations/0014_candidate_application_core.sql", + "sha256": "0e46b67d24dec348d0282b1a15822131c7b7f35a14204855a092e37661c67dad", + "bytes": 13889, + "lines": 307 + }, + { + "path": "database/migrations/0015_candidate_withdrawal_governance.sql", + "sha256": "54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320", + "bytes": 8236, + "lines": 192 + }, + { + "path": "database/migrations/0016_candidate_withdrawal_audit_envelope.sql", + "sha256": "af5d33853c591cdb52e11ec68c3ffe0e2c296e66582ced1e9c0f5c1150a0a768", + "bytes": 9104, + "lines": 243 + }, + { + "path": "docs/API_CONTRACT.md", + "sha256": "63533dff785da62b89e585d742a158e2aeb05913644f2bf9fb6486f281c2e589", + "bytes": 4555, + "lines": 76 + }, + { + "path": "docs/DATA_MODEL.md", + "sha256": "9565f012162a0111ab29c5135207cf01d38af64d3e1def8e1a937293b2e3edf6", + "bytes": 14913, + "lines": 90 + }, + { + "path": "docs/ERD.md", + "sha256": "f979d4de859d40332ef8cfa5182c9ea16b0244583c49250a5d77a588af574246", + "bytes": 8331, + "lines": 77 + }, + { + "path": "docs/OPERABILITY.md", + "sha256": "82b2d3e70cec371ef35e9e0f982ac40fef84351976bc04b863b81d27023d5a62", + "bytes": 11189, + "lines": 71 + }, + { + "path": "docs/PRD.md", + "sha256": "3ad85ae633cce0fc7a93af39b21d7a7c70bb2efa786da6b12f3c5327906e34f1", + "bytes": 5490, + "lines": 111 + }, + { + "path": "docs/SECURITY.md", + "sha256": "01918512d8882060e9cff0c4aa8206e0eccbdfb61cfd7f829331123c7a9fe6ac", + "bytes": 11185, + "lines": 64 + }, + { + "path": "docs/STORYBOARD.md", + "sha256": "6e4ffb0eb03a80343f50d363ffc43b34da9348a44232dd947a9ff416ea92a3d2", + "bytes": 1342, + "lines": 28 + }, + { + "path": "docs/STORYBOOK.md", + "sha256": "82f79029b3c2b7a45393bad5ba8fabe61014d4b6149c7d4e73f70ba447f885e9", + "bytes": 1389, + "lines": 50 + }, + { + "path": "docs/TEST_STRATEGY.md", + "sha256": "d0a0bc3b54ed0fc7973747987f1afb117d6144c390b51ed9370eb571972a33f8", + "bytes": 16534, + "lines": 135 + }, + { + "path": "docs/THREAT_MODEL.md", + "sha256": "f314f375c2e41252536de224c7bc7e4a10ab8f340cb86642724e7399e32f4252", + "bytes": 6736, + "lines": 23 + }, + { + "path": "docs/TRACEABILITY.md", + "sha256": "dbf6fd91375ea28e05456d2a0c9ba629506cbac6f52f5dfda61ae68db2395f7e", + "bytes": 11462, + "lines": 40 + }, + { + "path": "docs/TRD.md", + "sha256": "23697d88a4882698e1a2782b7da3f2ccd0d3cd2d6d1bffe89b6597dc16851077", + "bytes": 9064, + "lines": 101 + }, + { + "path": "docs/UML.md", + "sha256": "fe67c37aa88e5814ceb2db7e8f7d8d85ca27a994802efbb7c75164b387adf0a9", + "bytes": 5528, + "lines": 122 + }, + { + "path": "docs/USER_STORIES.md", + "sha256": "5535b39d8c71a36c81f78e2d6dbd90a2d32e6541790f0d28f6dd4baf3ea7b45f", + "bytes": 2670, + "lines": 37 + }, + { + "path": "docs/WIREFRAMES.md", + "sha256": "b03aa6419aeaf5d42a5698c4d43a434c1633b7ac6fd0b0bd0cda979077adc56e", + "bytes": 2005, + "lines": 77 + }, + { + "path": "docs/adr/0001-orgmetra-authoritative-hris-record.md", + "sha256": "0f8055b73c63d3130321415ad53233588ff952aabd1a88952b39c71747253572", + "bytes": 6108, + "lines": 53 + }, + { + "path": "docs/adr/0002-federated-cwl-integration-boundaries.md", + "sha256": "b77165f2aacfa6f4fde994baf77d5879c6da3e8dae4fd2db0ed912d60ae9b3b2", + "bytes": 4072, + "lines": 44 + }, + { + "path": "docs/adr/0003-bitemporal-hris-data-contract.md", + "sha256": "d7f2660616622c1a7994b28aa66d99d13836bcf755735595f9609a41282ab799", + "bytes": 4453, + "lines": 47 + }, + { + "path": "docs/adr/0004-employment-position-version-and-assignment-binding.md", + "sha256": "fee89e700414abe0b1cffec2acc687e5e014634db8f5ef9e8a92abba5c3cf182", + "bytes": 1872, + "lines": 30 + }, + { + "path": "docs/adr/0005-exclusive-employment-and-staffable-seats.md", + "sha256": "10f0eb409f4fa32d2c5bed2d583d8b43be8e61b5cbef0e927e5bebb5f5c8f85b", + "bytes": 2091, + "lines": 34 + }, + { + "path": "docs/adr/0006-governed-audit-outbox-envelope.md", + "sha256": "827298ddd997b47f78a89e89911ad8ea72e517b7714303637f0329b8cb52cabd", + "bytes": 14100, + "lines": 66 + }, + { + "path": "docs/adr/0007-governed-job-analysis-evidence.md", + "sha256": "953c6d2b9864a78b461b576092ec3f198f0b76709eaaaf7d0ed0182f95182c52", + "bytes": 5653, + "lines": 57 + }, + { + "path": "docs/adr/0008-purpose-bound-pii-authorization.md", + "sha256": "c5157d3bc58f3d8d29e03104dd15eb2911cc1bb66e2c92a935b26d7164648dc7", + "bytes": 5988, + "lines": 55 + }, + { + "path": "docs/adr/0009-performance-criterion-observation-scope.md", + "sha256": "1ac10bb2747b0a5b4d62f627825cfd7f978f3fa88d7575bffc23d56371240a64", + "bytes": 7057, + "lines": 57 + }, + { + "path": "docs/adr/0010-naruon-calendar-intent-boundary.md", + "sha256": "3e1050a964cc4ed76a1a0cf1e699ae5080acf8c9336f0decdd6d5229359db3c9", + "bytes": 3917, + "lines": 35 + }, + { + "path": "docs/adr/0011-bitemporal-workforce-composition.md", + "sha256": "1656ef8b57c836ef7936a8e9cb6a824681eb7563157a1ab0a29deb25849a457b", + "bytes": 5568, + "lines": 53 + }, + { + "path": "docs/adr/0012-governed-migration-handoff.md", + "sha256": "713855d670001d3964ecb36cc653830502fb1d82a58b9e39f564b6992dd2bd80", + "bytes": 5965, + "lines": 59 + }, + { + "path": "docs/adr/0013-governed-requisition-review-packet.md", + "sha256": "70bf2cbdf903a8793d6d8bc116a08331931090118341f42010236e09c6cc1802", + "bytes": 4693, + "lines": 46 + }, + { + "path": "docs/adr/0014-job-analysis-snapshot-persistence.md", + "sha256": "a7ab6fee50aaa63f7f407516a4cb39885faeb0fc6e5035ee8fc352ed73430105", + "bytes": 5365, + "lines": 49 + }, + { + "path": "docs/adr/0026-normalized-candidate-application.md", + "sha256": "ecc901bf444c06e27c7e3dbdbeb6cf9bbded2f9450c52563721464ac6dbca081", + "bytes": 9945, + "lines": 73 + }, + { + "path": "docs/adr/0027-governed-candidate-withdrawal.md", + "sha256": "48d7c949989b80646f690d4185150ad50e8c31a3c29761a6be95b09849ba9d72", + "bytes": 7850, + "lines": 81 + }, + { + "path": "docs/adr/README.md", + "sha256": "19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e", + "bytes": 2310, + "lines": 22 + }, + { + "path": "docs/doctoring/REFERENCES.md", + "sha256": "929f7ee36df16279f028f726fcf039982180deb377746fe3804f3c0d090778d5", + "bytes": 6352, + "lines": 69 + }, + { + "path": "docs/doctoring/candidate-application-references.md", + "sha256": "202ca866ee16f6e61b11fcbe7ac712d308da189284b73c34a5121fa1e7495b0c", + "bytes": 2962, + "lines": 24 + }, + { + "path": "docs/doctoring/candidate-withdrawal-references.md", + "sha256": "a49be5ae37725158d7da7a82f6cc60df122f2cb086e1234bcecd5c85a41242c2", + "bytes": 3923, + "lines": 39 + }, + { + "path": "docs/superpowers/plans/2026-08-15-orgmetra-foundation-implementation-plan.md", + "sha256": "b64f21abb19373e780db8b9e64deb8ba9a6219ccf9625a651f25407b8691fcbd", + "bytes": 8227, + "lines": 226 + }, + { + "path": "docs/superpowers/specs/2026-08-15-orgmetra-foundation-design.md", + "sha256": "4a0e1a7943e40d12bd3082db3757045b4085e5a089fea7bc0d8a1565ffcbcf1d", + "bytes": 6237, + "lines": 187 + }, + { + "path": "docs/traceability/candidate-application-core.md", + "sha256": "3ea6f15ea0cef7695bd4aca88a7da01f0c19bb0f4dac87b401d82b02bf152e14", + "bytes": 6877, + "lines": 35 + }, + { + "path": "docs/traceability/candidate-withdrawal.md", + "sha256": "cb83b5ac0daa38a1cdc4c1667660b3d36532811489707d1f0f32b0c548594156", + "bytes": 4891, + "lines": 32 + }, + { + "path": "package.json", + "sha256": "59ae9e3e67c3fba9320cb18439692395cdfd16ae5c24e3c4cf30d77d63ebabb5", + "bytes": 388, + "lines": 9 + }, + { + "path": "packages/hris-kernel/src/orgmetra_hris_kernel/audit.py", + "sha256": "3e5b7190cf857dc8c1fc7e898cef303060f34aabee6c27a9034d4d9650e33190", + "bytes": 7707, + "lines": 160 + }, + { + "path": "packages/hris-kernel/tests/test_audit_outbox.py", + "sha256": "5928dd7b97fe38d6b7472ce62966437e339058a59c3b301a93a7b5c05432b40c", + "bytes": 7556, + "lines": 200 + }, + { + "path": "schemas/openapi.yaml", + "sha256": "09c1e43486779198574fe31b8bcabbd1c1f74beec7bf86245ae578061619838f", + "bytes": 29503, + "lines": 1020 + }, + { + "path": "scripts/foundation-contract-core.mjs", + "sha256": "fbc590c889baacc420ea22dfa03cb54b74053f544379bf17056efe4d5477748f", + "bytes": 29888, + "lines": 721 + }, + { + "path": "scripts/foundation-contract.mjs", + "sha256": "5242dcdbe0935775edf074462c82600e9bc4927d9fdc50c47727af915fd4b23a", + "bytes": 218, + "lines": 6 + }, + { + "path": "tests/dispatcher-inventory.test.mjs", + "sha256": "09f5e64410e6b7a26bf8d6ce61c50b737da2ea85d955f91eba63aa21f1537261", + "bytes": 1597, + "lines": 34 + }, + { + "path": "tests/foundation-contract.test.mjs", + "sha256": "3d7deb18dc5f4107e22d61718bab8eab5d7f04a46bd0c02d3873a3687da803f4", + "bytes": 16371, + "lines": 427 + }, + { + "path": "tests/openapi-contract.test.mjs", + "sha256": "80c1610ef1c189fa325e55389501e0e51531ddf61ee335bb94d9cb3aa55a9fdc", + "bytes": 6438, + "lines": 195 + }, + { + "path": "tests/test_audit_outbox_hardening_postgres.sh", + "sha256": "518ba2f37ba6292943e5abe22c2599452b2f031a42e453b2493aedf8714421a0", + "bytes": 13396, + "lines": 333 + }, + { + "path": "tests/test_audit_outbox_postgres.sh", + "sha256": "e57a04920a0ba97fa6a06752d15ea150016ab8d44099e998c5c4f4067592b4d2", + "bytes": 13443, + "lines": 357 + }, + { + "path": "tests/test_bitemporal_postgres.sh", + "sha256": "7684b8c2ff52c044c081135515bd5aabbfd00e2daad0d471b0868701af2df6cc", + "bytes": 8209, + "lines": 230 + }, + { + "path": "tests/test_candidate_application_decision_boundary_postgres.sh", + "sha256": "887f20aba2c0e376fa0678da33e606a27cbf3bd8d149dd8cb2654fc264f0f416", + "bytes": 3954, + "lines": 98 + }, + { + "path": "tests/test_candidate_application_postgres.sh", + "sha256": "69e18d46a76a35afa0d97e855bff79100a51f4f2ab603707db2be8e552b5c686", + "bytes": 14311, + "lines": 236 + }, + { + "path": "tests/test_candidate_application_rls_postgres.sh", + "sha256": "0f0ae93d28bb88430e377c05654250d2567ea0ff6c9b5c41365779fe91e2a563", + "bytes": 4637, + "lines": 118 + }, + { + "path": "tests/test_candidate_withdrawal_migration_upgrade_postgres.sh", + "sha256": "4f281601b349f65e54a7f14506d5d429739ab7e9144c6aaaee0fcc3c9e872374", + "bytes": 2681, + "lines": 70 + }, + { + "path": "tests/test_candidate_withdrawal_postgres.sh", + "sha256": "019e07ca343d90a35f5f879309c53a298d5415b96de01ccfff40586f580510e3", + "bytes": 7818, + "lines": 228 + }, + { + "path": "tests/test_candidate_withdrawal_security_postgres.sh", + "sha256": "0eaa30a0053859e1cf8e573ca2189bd569b87c940a55478a8f80370964f47ec7", + "bytes": 12375, + "lines": 317 + }, + { + "path": "tests/test_candidate_worker_conversion_postgres.sh", + "sha256": "681cb74d6cfa859ed92c6c2439881ea20c430ef8df94ec662e2807761a377f90", + "bytes": 14673, + "lines": 344 + }, + { + "path": "tests/test_criterion_observation_scope_postgres.sh", + "sha256": "0ee9539ee57f840c27d08009f7868cdc8662669df78a01dbc8be39216b8f1a3d", + "bytes": 17811, + "lines": 469 + }, + { + "path": "tests/test_evidence_sealing_postgres.sh", + "sha256": "57d16b632a0c60ffdcb4842ceb1cfe25d19c54cefeeefb622ff4fa6e83441ad7", + "bytes": 11349, + "lines": 370 + }, + { + "path": "tests/test_job_analysis_snapshot_postgres.sh", + "sha256": "ca9c323a1dd68cfc520277efbbb7495e37fb3ca027890928c8624e5b4f57403f", + "bytes": 13542, + "lines": 296 + }, + { + "path": "tests/test_operational_uuid_postgres.sh", + "sha256": "7378f98f0d4b3000e8ea641d8701f1540dbad71410b3637d81d799969e0f6ff7", + "bytes": 3346, + "lines": 101 + }, + { + "path": "tests/test_outbox_claim_postgres.sh", + "sha256": "1027806d436ebfe34e108c25b6a4001f43b9550f1d70057c6c0d7974323b0c9b", + "bytes": 14817, + "lines": 429 + }, + { + "path": "tests/test_outbox_dead_letter_postgres.sh", + "sha256": "0d728d578e64252e6079f2d141ddaa7fa9cfbf9784e625832273596d69a6e13d", + "bytes": 14008, + "lines": 377 + }, + { + "path": "tests/test_people_mutation_idempotency_postgres.sh", + "sha256": "3f57e12f80bd1b034c9aac54b669d8530106e3e26b3795689671fb53807b3cd5", + "bytes": 16191, + "lines": 381 + }, + { + "path": "tests/test_tenant_isolation_postgres.sh", + "sha256": "dd649435ef8ab9e57f0609c101917e36656a6d40d63de9bcdbdac23d764f6c3a", + "bytes": 15134, + "lines": 388 + }, + { + "path": "tests/test_validity_study_case_postgres.sh", + "sha256": "0070ad58300323c7f9900c5645e0df3106b36ccd245ae686e982c2fd6fa4dc02", + "bytes": 14708, + "lines": 301 + }, + { + "path": "tests/validate_repository.py", + "sha256": "18e977ed50d842eda3b75dd77ff167fe0f81938ab1bab6a6f31d5c5eabaa22a9", + "bytes": 29271, + "lines": 684 + } + ] +} diff --git a/scripts/foundation-contract-core.mjs b/scripts/foundation-contract-core.mjs index cb8be431e..69e679fda 100644 --- a/scripts/foundation-contract-core.mjs +++ b/scripts/foundation-contract-core.mjs @@ -74,6 +74,7 @@ export const REQUIRED_FILES = Object.freeze([ 'database/migrations/0013_job_analysis_snapshot.sql', 'database/migrations/0014_candidate_application_core.sql', 'database/migrations/0015_candidate_withdrawal_governance.sql', + 'database/migrations/0016_candidate_withdrawal_audit_envelope.sql', 'docs/traceability/candidate-application-core.md', 'docs/traceability/candidate-withdrawal.md', '.github/workflows/candidate-application-quality.yml', @@ -104,6 +105,7 @@ export const REQUIRED_FILES = Object.freeze([ 'tests/test_job_analysis_snapshot_postgres.sh', 'tests/test_candidate_withdrawal_postgres.sh', 'tests/test_candidate_withdrawal_security_postgres.sh', + 'tests/test_candidate_withdrawal_migration_upgrade_postgres.sh', 'tests/validate_repository.py' ]); diff --git a/tests/test_candidate_withdrawal_migration_upgrade_postgres.sh b/tests/test_candidate_withdrawal_migration_upgrade_postgres.sh new file mode 100644 index 000000000..a5be2ac18 --- /dev/null +++ b/tests/test_candidate_withdrawal_migration_upgrade_postgres.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${DATABASE_URL:=postgresql://orgmetra:orgmetra@localhost:5432/orgmetra}" + +# Migration 0008 is already applied in deployed databases. Prove the new +# withdrawal envelope is rejected by that legacy validator and accepted only +# after the forward repair migration is applied. +for migration in database/migrations/000{1..8}_*.sql; do + psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -f "${migration}" +done + +withdrawal_validation() { + psql "${DATABASE_URL}" -Atq <<'SQL' +WITH envelope AS ( + SELECT jsonb_build_object( + 'specversion', '1.0', + 'id', '00000000-0000-4000-8000-0000000000c1', + 'source', 'urn:orgmetra:talent_acquisition', + 'type', 'orgmetra.candidate.application_withdrawn', + 'subject', 'candidate_withdrawal_record:00000000-0000-4000-8000-0000000000c2', + 'time', '2026-08-21T09:10:00Z', + 'datacontenttype', 'application/json', + 'orgmetratenant', '10000000-0000-7000-8000-000000000001', + 'orgmetraactor', 'candidate:00000000-0000-4000-8000-0000000000c3', + 'orgmetrapurpose', 'candidate_withdrawal', + 'orgmetrareason', 'candidate_requested', + 'orgmetraevidence', 'candidate_withdrawal_evidence:00000000-0000-4000-8000-0000000000c4', + 'data', jsonb_build_object( + 'high_impact', false, + 'result_code', 'application_withdrawn', + 'evidence_version', 1, + 'identity_resolution_reference', 'identity_resolution:00000000-0000-4000-8000-0000000000c5', + 'identity_resolution_digest', repeat('a', 64), + 'withdrawal_evidence_digest', repeat('b', 64) + ) + )::text AS canonical_event_json +), payload AS ( + SELECT + canonical_event_json, + encode(digest(convert_to(canonical_event_json, 'UTF8'), 'sha256'), 'hex') + AS event_digest + FROM envelope +) +SELECT public.validate_audit_event_envelope( + canonical_event_json, + '00000000-0000-4000-8000-0000000000c1'::uuid, + '10000000-0000-7000-8000-000000000001'::uuid, + event_digest +) +FROM payload; +SQL +} + +legacy_result="$(withdrawal_validation)" +if [[ "${legacy_result}" != "f" ]]; then + echo "legacy migration 0008 unexpectedly accepted a candidate withdrawal envelope: ${legacy_result}" >&2 + exit 1 +fi + +psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 \ + -f database/migrations/0016_candidate_withdrawal_audit_envelope.sql + +upgraded_result="$(withdrawal_validation)" +if [[ "${upgraded_result}" != "t" ]]; then + echo "forward migration 0016 did not enable candidate withdrawal envelopes: ${upgraded_result}" >&2 + exit 1 +fi + +echo "candidate withdrawal migration upgrade contract passed" diff --git a/tests/validate_repository.py b/tests/validate_repository.py index f12c82f3b..ddfd29a0d 100644 --- a/tests/validate_repository.py +++ b/tests/validate_repository.py @@ -78,6 +78,7 @@ "database/migrations/0013_job_analysis_snapshot.sql", "database/migrations/0014_candidate_application_core.sql", "database/migrations/0015_candidate_withdrawal_governance.sql", + "database/migrations/0016_candidate_withdrawal_audit_envelope.sql", "docs/traceability/candidate-application-core.md", "docs/traceability/candidate-withdrawal.md", ".github/workflows/candidate-application-quality.yml", @@ -108,6 +109,7 @@ "tests/test_job_analysis_snapshot_postgres.sh", "tests/test_candidate_withdrawal_postgres.sh", "tests/test_candidate_withdrawal_security_postgres.sh", + "tests/test_candidate_withdrawal_migration_upgrade_postgres.sh", "tests/validate_repository.py", ] From b855af94f2333a4f331aa4b2a048bd2f8ff7e338 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 11:42:10 +0900 Subject: [PATCH 16/18] fix(workflow): trigger withdrawal migration upgrade checks --- .github/workflows/candidate-withdrawal-quality.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/candidate-withdrawal-quality.yml b/.github/workflows/candidate-withdrawal-quality.yml index b1f6ef160..ff43ddb51 100644 --- a/.github/workflows/candidate-withdrawal-quality.yml +++ b/.github/workflows/candidate-withdrawal-quality.yml @@ -9,6 +9,7 @@ on: - "database/migrations/**" - "tests/test_candidate_withdrawal_postgres.sh" - "tests/test_candidate_withdrawal_security_postgres.sh" + - "tests/test_candidate_withdrawal_migration_upgrade_postgres.sh" - ".github/workflows/candidate-withdrawal-quality.yml" - "docs/adr/0027-governed-candidate-withdrawal.md" - "docs/traceability/candidate-withdrawal.md" From 65d86274d3c244344feab6811f236ffdc6ffcb18 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 12:01:48 +0900 Subject: [PATCH 17/18] docs(adr): align normalized application maturity --- docs/adr/README.md | 2 +- manifest.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/adr/README.md b/docs/adr/README.md index 26ee61920..625a77684 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -18,5 +18,5 @@ | [0014](0014-job-analysis-snapshot-persistence.md) | Persist governed job-analysis snapshots | Accepted on active implementation branch | | [0017](0017-governed-offer-approval.md) | Governed offer approval evidence | Proposed | | [0025](0025-governed-candidate-evidence-intake.md) | Governed candidate evidence intake | Proposed | -| [0026](0026-normalized-candidate-application.md) | Normalize the candidate application lifecycle | Accepted on active implementation branch | +| [0026](0026-normalized-candidate-application.md) | Normalize the candidate application lifecycle | Proposed | | [0027](0027-governed-candidate-withdrawal.md) | Govern candidate-initiated application withdrawal as immutable evidence | Proposed | diff --git a/manifest.json b/manifest.json index c676686f2..a090b50be 100644 --- a/manifest.json +++ b/manifest.json @@ -359,8 +359,8 @@ }, { "path": "docs/adr/README.md", - "sha256": "19a52a0f8fc381d32258811b630a10e6aec550e750fa60f70c9b7075e37a731e", - "bytes": 2310, + "sha256": "213125c10cd13a30cfd5c2a3c62b64a1863b0b8735618377786763763139d23b", + "bytes": 2278, "lines": 22 }, { From ee5a56db3f64d972a08fab367fbed3f5005fa8f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 21:09:16 +0900 Subject: [PATCH 18/18] fix(withdrawal): own recorded evidence time --- CHANGELOG.md | 1 + .../0015_candidate_withdrawal_governance.sql | 3 ++ .../adr/0027-governed-candidate-withdrawal.md | 5 ++++ docs/traceability/candidate-withdrawal.md | 2 +- manifest.json | 28 +++++++++---------- tests/test_candidate_withdrawal_postgres.sh | 14 +++++++++- 6 files changed, 37 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8da8eb2f7..fb740b2db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to Orgmetra will be documented in this file. - Active-PR governed Job Analysis persistence/API on the canonical `JobAnalysisSnapshot` model: migration `0013_job_analysis_snapshot.sql` stores immutable tenant-scoped snapshot, Task, KSAO, Task–KSAO, FJA and write-command evidence; `POST /v1/tenants/{tenant_record_id}/job-analysis-snapshots` and matching GET enforce purpose-bound Keyverse scope, authenticated-principal actor authority, bounded/strict JSON handling, transactional Idempotency-Key serialization, parent-scope fail-closed integrity, forced RLS, and atomic audit/outbox evidence. ADR 0014 records the persistence decision while ADR 0007 remains the domain/evidence authority; validated evidence still requires accountable human review and non-LLM provenance, and the service does not make a high-impact employment decision. - Active-PR candidate-withdrawal persistence now binds identity-resolution reference/digest, withdrawal evidence reference/digest, and evidence version into the candidate-specific immutable audit envelope as well as the append-only withdrawal fact; generic audit envelopes retain their exact existing data contract. - Active-PR candidate-withdrawal persistence now delivers its audit-envelope extension through forward-only migration `0016_candidate_withdrawal_audit_envelope.sql`, preserving already-applied migration `0008` while proving the legacy-to-upgraded validator path in a disposable PostgreSQL contract. +- Active-PR candidate-withdrawal persistence now assigns `recorded_at` from PostgreSQL transaction time at the insert trigger, rejecting caller-supplied backdated or future system-recorded evidence while preserving candidate-declared `withdrawn_at` chronology. - Active-PR `orgmetra_selection_review` packet for PII-minimized, evidence-bound human selection review: canonical operational tenant identity, UUID-backed opaque candidate/Job/sealed-evidence/reviewer references, explicit purpose/reason/evidence version, deterministic canonical JSON and SHA-256 correlation, mandatory human decision state, redacted packet repr, and provenance-paired model evidence that remains `untrusted_draft`, with exact 100% owned statement and branch coverage required by its quality gate. - Active performance-criterion scope hardening: `criterion_observation_scope_guard` rejects criterion outcomes for a Job the worker did not effectively hold at the observation date, observations before the relevant assignment, and observations outside the referenced performance cycle while preserving valid multiple-assignment cases and existing bitemporal correction semantics. The guard evaluates current-recorded facts, derives the date coordinate from `observed_at` in UTC so session `TimeZone` cannot alter the result, uses a trusted function search path, and adds no PII or automated employment decision authority. The Foundation PostgreSQL contract also rejects a closed `recorded_to` on each time-coordinate lookup and proves UTC midnight plus non-UTC session `TimeZone` boundaries. - Bitemporal tenant-scoped organization hierarchy validation that rejects visible indirect parent cycles and reuses single-valued recorded-time reconstruction before graph traversal. diff --git a/database/migrations/0015_candidate_withdrawal_governance.sql b/database/migrations/0015_candidate_withdrawal_governance.sql index 8a3c8f969..7943a18d6 100644 --- a/database/migrations/0015_candidate_withdrawal_governance.sql +++ b/database/migrations/0015_candidate_withdrawal_governance.sql @@ -75,6 +75,9 @@ DECLARE audit_event_envelope jsonb; audit_event_time timestamptz; BEGIN + -- recorded_at is database-owned evidence, not a caller-provided timestamp. + NEW.recorded_at := pg_catalog.transaction_timestamp(); + SELECT application_record.submitted_at INTO application_submitted_at FROM public.candidate_application_record AS application_record diff --git a/docs/adr/0027-governed-candidate-withdrawal.md b/docs/adr/0027-governed-candidate-withdrawal.md index d3dbdba9b..ea09390b9 100644 --- a/docs/adr/0027-governed-candidate-withdrawal.md +++ b/docs/adr/0027-governed-candidate-withdrawal.md @@ -28,6 +28,10 @@ A withdrawal row binds: 6. one immutable tenant-local `audit_event_record`; and 7. the system `recorded_at` instant. +The insert guard always assigns `recorded_at` from PostgreSQL transaction time; +callers cannot backdate or future-date the system-recorded evidence by supplying +the column explicitly. + Exactly one withdrawal may exist for one application. The row is append-only: UPDATE, DELETE, and TRUNCATE are rejected. The table forces row-level security using Orgmetra's existing tenant-context function. Before insert, `validate_candidate_withdrawal_evidence()` re-resolves the tenant-local application and immutable audit event. It rejects withdrawal before application submission and requires the audit envelope to bind the exact withdrawal record, candidate actor, identity-resolution reference/digest, withdrawal evidence reference/digest, evidence version, purpose `candidate_withdrawal`, reason `candidate_requested`, event time, and non-high-impact `application_withdrawn` result. A matching transactional outbox delivery record must already exist. @@ -51,6 +55,7 @@ If product requirements later allow an authorized representative to withdraw on - Candidate withdrawal is represented without overwriting candidate identity or smuggling a terminal outcome into an ordinary workflow stage. - Staff callers cannot record a withdrawal merely by choosing a `withdrawn` status or by creating a generic audit envelope with a staff actor. - Audit/event time and immutable evidence remain correlated with the exact withdrawal row. +- System-recorded time is assigned by PostgreSQL at insert and cannot be supplied by the caller. - One application cannot accumulate contradictory multiple withdrawal facts. - Missing tenant context fails closed under forced RLS; tenant Alpha and Beta withdrawals are mutually invisible to a `NOBYPASSRLS` reader. - The boundary remains dependent on the application layer's authorized identity adapter for actual claimant authentication and identity resolution. This ADR does not claim otherwise. diff --git a/docs/traceability/candidate-withdrawal.md b/docs/traceability/candidate-withdrawal.md index 0dbc1e3e5..5d33207c2 100644 --- a/docs/traceability/candidate-withdrawal.md +++ b/docs/traceability/candidate-withdrawal.md @@ -16,7 +16,7 @@ | Existing databases receive the withdrawal envelope rule | forward-only `0016_candidate_withdrawal_audit_envelope.sql` replaces the already-deployed validator after legacy `0008` has run; `0008` itself remains immutable | `test_candidate_withdrawal_migration_upgrade_postgres.sh` proves legacy rejection followed by upgraded acceptance | | Candidate withdrawal is not an employer high-impact decision | audit envelope requires `high_impact=false` and has no `orgmetraconfirmation`; employer outcomes remain in `selection_decision` | `test_candidate_withdrawal_postgres.sh`; migration trigger predicates | | Raw ATS workflow cannot smuggle terminal withdrawal | PR #66 stage vocabulary remains non-terminal; PR #67 does not add `withdrawn` to it | `test_candidate_withdrawal_postgres.sh` attempts raw `withdrawn` stage insertion and requires `candidate_application_stage_code_check` | -| Application chronology is preserved | withdrawal cannot predate application `submitted_at`; audit time must equal `withdrawn_at` and not exceed `recorded_at` | migration trigger exercised by happy-path and anti-forgery contracts | +| Application chronology is preserved | withdrawal cannot predate application `submitted_at`; audit time must equal `withdrawn_at` and not exceed database-owned `recorded_at`, which the insert trigger assigns from transaction time | migration trigger exercised by happy-path, caller-future-time, and anti-forgery contracts | | One application has at most one withdrawal fact | unique `(tenant_record_id, candidate_application_record_id)` | security test uses a second otherwise-valid audit event so the duplicate fails specifically at `candidate_withdrawal_application_unique` | | Tenant isolation fails closed | FORCE RLS with existing `current_tenant_record_id()` policy | security test uses a `NOBYPASSRLS` reader: no context sees zero; Alpha and Beta each see only their own row | | Validation runs on exact candidate source | pinned checkout plus explicit `git rev-parse HEAD` equality proof | `.github/workflows/candidate-withdrawal-quality.yml` | diff --git a/manifest.json b/manifest.json index a090b50be..ac52cd20a 100644 --- a/manifest.json +++ b/manifest.json @@ -47,9 +47,9 @@ }, { "path": "CHANGELOG.md", - "sha256": "18f999729647bad231b926874af9d85c75cd9ea60a43993fcce11de1baa794bf", - "bytes": 18816, - "lines": 79 + "sha256": "3552972c2cbbe2e0c0dd085ed2ff7e7d1828b06e767e6b14e1ca9fda9c2391de", + "bytes": 19077, + "lines": 80 }, { "path": "CLAUDE.md", @@ -161,9 +161,9 @@ }, { "path": "database/migrations/0015_candidate_withdrawal_governance.sql", - "sha256": "54839ddc972040ec30261a78e16aefb173ed6065808bd65783f185ee042bc320", - "bytes": 8236, - "lines": 192 + "sha256": "5b5a6fc372734be8fd7e0c994cba5a640e029037f2042bfc8f3f61ba9e29b005", + "bytes": 8376, + "lines": 195 }, { "path": "database/migrations/0016_candidate_withdrawal_audit_envelope.sql", @@ -353,9 +353,9 @@ }, { "path": "docs/adr/0027-governed-candidate-withdrawal.md", - "sha256": "48d7c949989b80646f690d4185150ad50e8c31a3c29761a6be95b09849ba9d72", - "bytes": 7850, - "lines": 81 + "sha256": "8170601fba5907bcf055a03941d3e736e6b0c58f7d1b6b57e26d1b2912e210a6", + "bytes": 8132, + "lines": 86 }, { "path": "docs/adr/README.md", @@ -401,8 +401,8 @@ }, { "path": "docs/traceability/candidate-withdrawal.md", - "sha256": "cb83b5ac0daa38a1cdc4c1667660b3d36532811489707d1f0f32b0c548594156", - "bytes": 4891, + "sha256": "100d752880f0d4341676b05d46334df10709a4b808c76969dd3aa7dfd63e638b", + "bytes": 4983, "lines": 32 }, { @@ -503,9 +503,9 @@ }, { "path": "tests/test_candidate_withdrawal_postgres.sh", - "sha256": "019e07ca343d90a35f5f879309c53a298d5415b96de01ccfff40586f580510e3", - "bytes": 7818, - "lines": 228 + "sha256": "5447dbe60ead96ece5502741888a4696474730c1e8efc4f46577d7ec2990463b", + "bytes": 8371, + "lines": 240 }, { "path": "tests/test_candidate_withdrawal_security_postgres.sh", diff --git a/tests/test_candidate_withdrawal_postgres.sh b/tests/test_candidate_withdrawal_postgres.sh index ac4b9e4d5..ddf4ba6bd 100644 --- a/tests/test_candidate_withdrawal_postgres.sh +++ b/tests/test_candidate_withdrawal_postgres.sh @@ -161,7 +161,8 @@ INSERT INTO candidate_withdrawal_record ( 1, TIMESTAMPTZ '2026-08-21 09:20:00+00', '70000000-0000-7000-8000-000000000001', - TIMESTAMPTZ '2026-08-21 09:20:01+00' + -- The trigger must replace this caller-supplied future system time. + TIMESTAMPTZ '2099-01-01 00:00:00+00' ); SQL @@ -176,6 +177,17 @@ if [[ "${persisted_count}" != "1" ]]; then exit 1 fi +recorded_at_is_system_time="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SELECT (recorded_at <= transaction_timestamp())::text +FROM candidate_withdrawal_record +WHERE tenant_record_id = '10000000-0000-7000-8000-000000000001'::uuid + AND candidate_withdrawal_record_id = '72000000-0000-7000-8000-000000000001'::uuid; +")" +if [[ "${recorded_at_is_system_time}" != "true" ]]; then + echo "candidate withdrawal accepted caller-controlled future recorded_at" >&2 + exit 1 +fi + set +e raw_stage_output="$({ psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 <<'SQL' INSERT INTO candidate_application_stage_record (