diff --git a/.github/requirements/foundation-test.txt b/.github/requirements/foundation-test.txt index 40d926005..16e84cd0e 100644 --- a/.github/requirements/foundation-test.txt +++ b/.github/requirements/foundation-test.txt @@ -1,4 +1,4 @@ -# Reviewed Foundation CI test toolchain for CPython 3.14 on GitHub-hosted Ubuntu x86_64. +# Reviewed Foundation CI test/build toolchain for CPython 3.14 on GitHub-hosted Ubuntu x86_64. # Version and artifact hash changes must be reverified against the official PyPI release JSON. coverage==7.14.2 --hash=sha256:cda36d8e7bfd63b3e44e75163265429caa5d935b672b00f71bccc8c010518c64 iniconfig==2.3.0 --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 @@ -7,3 +7,4 @@ pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d83 Pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 pytest==9.1.1 --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c pytest-cov==7.1.0 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 +setuptools==82.0.1 --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb diff --git a/.github/workflows/foundation-ci.yml b/.github/workflows/foundation-ci.yml index 6b475d6f2..4f6d05360 100644 --- a/.github/workflows/foundation-ci.yml +++ b/.github/workflows/foundation-ci.yml @@ -68,6 +68,7 @@ jobs: PYTHONPATH=packages/selection-review/src COVERAGE_FILE=/tmp/orgmetra-selection-review.coverage python -m pytest -c packages/selection-review/pyproject.toml packages/selection-review/tests PYTHONPATH=services/job-analysis-api/src:packages/hris-kernel/src:packages/keyverse-adapter/src COVERAGE_FILE=/tmp/orgmetra-job-analysis-api.coverage python -m pytest -c services/job-analysis-api/pyproject.toml services/job-analysis-api/tests PYTHONPATH=services/people-api/src:packages/hris-kernel/src:packages/keyverse-adapter/src COVERAGE_FILE=/tmp/orgmetra-people-api.coverage python -m pytest -c services/people-api/pyproject.toml services/people-api/tests + PYTHONPATH=services/workforce-validation-api/src:packages/keyverse-adapter/src COVERAGE_FILE=/tmp/orgmetra-workforce-validation-api.coverage python -m pytest -c services/workforce-validation-api/pyproject.toml services/workforce-validation-api/tests - name: Run PostgreSQL contracts in isolated containers env: PGPASSWORD: orgmetra @@ -83,6 +84,7 @@ jobs: test_audit_outbox_hardening_postgres.sh test_candidate_worker_conversion_postgres.sh test_validity_study_case_postgres.sh + test_workforce_validation_owner_schema_postgres.sh test_criterion_observation_scope_postgres.sh test_people_mutation_idempotency_postgres.sh test_job_analysis_snapshot_postgres.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 16454da3d..508ecab81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to Orgmetra will be documented in this file. ### Added - 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 governed Job Analysis persistence/API on the canonical `JobAnalysisSnapshot` model: migration `0013_job_analysis_snapshot.sql` stores immutable tenant-scoped snapshot, 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 `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. @@ -18,7 +18,7 @@ All notable changes to Orgmetra will be documented in this file. - `employment_record_version.employment_concurrency_code` constrained to `exclusive` or `concurrent`. - ADR 0005 for exclusive employment and staffable seats. - `orgmetra_hris_kernel` 0.3.0 with identity-scoped bitemporal resolution, assignment-employment coverage, allocation-portfolio checks, and a Memorial Hospital RN correction case at 100% statement and branch coverage. -- `employment_record_version` and `position_record_version` so employment and position identity stay stable across retroactive corrections. +- `employment_record_version` and `position_record_version` so corrections no longer mint a new employment or position identifier. - `assignment_record.employment_record_id` bound to the same person as the covering employment. - `orgmetra_keyverse_adapter` that binds an opaque Keyverse subject to a person and rejects passwords, passkeys, and tokens. - Design tokens for the repeating HR actions: approve, review, correct, request evidence, compare, export, and escalate. @@ -37,6 +37,9 @@ All notable changes to Orgmetra will be documented in this file. ### Changed +- Active-PR Workforce Validation wheel acceptance now builds both owned distributions from temporary source copies, proving RECORD integrity without mutating the checkout. Offline wheel subprocesses also treat Python warnings as errors, and the service publishes its Apache-2.0 license through the current SPDX string form instead of deprecated setuptools table metadata. +- Active-PR Workforce Validation coverage recovery now exercises exact owner-capability admission, immutable release-view identities, version and chronology rejection, digest independence, approximation semantics, final-weight component corroboration, and base/nonresponse/trimming supersession edge contracts. An earlier exact head passed 1,306 tests with 4,487/4,487 owned statements and 1,070/1,070 owned branches covered; subsequent authorized-view sealing repairs add new production and hostile-regression branches, so that verdict does not transfer and final exact-head functional, statement, branch, docstring, and edge evidence must be reacquired before integration. +- Active-PR Workforce Validation acceptance fixtures now track released owner contracts, supersession cutovers, owner-read field sets, and standard-library timezone-provider failures exactly; wheel acceptance validates every owned wheel's internal identity and metadata before producing any outer artifact hash. - Consolidated repository-owned PR validation from twelve workflows into one Foundation CI job, while keeping the dual-cluster recovery rehearsal separately path-scoped. Central required review and security workflows remain organization-owned. - New predictive-validity membership must use one normalized worker-level case; the three independent validity-study decision/evidence/outcome link relations are historical read surfaces only and can no longer accept new rows. A case insert also rejects a criterion observation whose recorded interval is already closed at `linked_at`. - Canonicalized service identifiers as two-or-more-word `snake_case` across architecture, deployment, ACL, metrics, and client contracts. @@ -57,6 +60,9 @@ All notable changes to Orgmetra will be documented in this file. ### Security +- Active-PR Workforce Validation final-weight adjustment admission now fails closed for any material adjustment code without a released governed owner-evidence family. Evidence digests are integrity coordinates, not owner-record locators; adding a future adjustment family requires a versioned receipt namespace, exact reference/version/digest identity, purpose-bound deterministic owner resolution, canonical reconstruction, and binding/corroboration of transform semantics plus release/currentness before it can enter a released final-weight lineage. The component-binding owner now also requires every governed adjustment receipt binding to be contiguous and one-based, so an intrinsically incomplete sequence such as `(1, 3)` cannot be persisted and deferred to the later corroboration service. The corroborator requires a binding for every admitted adjustment and removes the obsolete path that treated an ungoverned generic transform as owner-local. +- Active-PR Workforce Validation all 24 exported resolver-issued evidence views now keep their issuance seals in closure-private runtime state. Package-wide hostile marker-copy regressions reject importable module sealing capabilities and caller-populated raw exact-runtime objects; purpose authorization, canonical owner reconstruction, scientific-coordinate matching, effective-time currentness, field minimization, detached identities, and marker-last issuance remain unchanged. Runtime view type or seal is not durable authorization. +- Active-PR Workforce Validation authorized evidence views reject low-level base-constructor forging across the complete 24-class public export census. Every exported `*View` is a sealed non-tuple data object whose public constructor rejects, whose raw allocations cannot expose state, and whose only supported issuer remains its purpose-authorized owner-resolution path. The executable package-surface census now fails closed on an added, removed, tuple-backed, unsealed, or mutable public view; `CalibrationBenchmarkAuthorityView` was the final omission exposed by that census and is covered by its own issuance-integrity regression. - Predictive-validity cases fail closed when selection evidence, Job scope, study criterion, converted worker, or system-recorded visibility does not match; the normalized case relation is tenant-qualified, append-only, TRUNCATE-protected, and forced through row-level security. - Purpose-bound PII authorization now fails closed across active tenant, authenticated actor tenant, resource tenant, resource kind, purpose, operation, operation-specific Keyverse scope, and requested-field subset; malformed/wildcard-like attributes, mutable field/scope collections, reserved UUID sentinels, and cross-tenant confused-deputy contexts are rejected before protected values are returned. Authorization requests and allow/deny evidence now also require and preserve one namespaced opaque target-resource reference, so immutable audit correlation identifies the exact HR record without copying its protected values. Authorization evidence otherwise contains governance metadata and field names only, with stable denial reasons and actionable next steps rather than PII. - LLM output constrained to draft evidence. @@ -74,4 +80,4 @@ All notable changes to Orgmetra will be documented in this file. ### Notes -- Protected `develop` at `e7ddb7a78a5e1460410005d10f43ebf18c5e12e4` includes normalized validity-study and criterion integrity, bitemporal workforce composition, governed candidate-to-worker conversion, purpose-bound PII authorization, GET-only People reads, governed People mutation/idempotency API, and the accepted ADR 0001–0003 source expansion integrated by #37. Job Analysis persistence/API and the selection-review packet remain active-PR truth until their unchanged exact heads satisfy fresh gates and merge. +- Protected `develop` at `eb9757f8649aaad026a9865508d9aad50c1a7a4f` includes normalized validity-study and criterion integrity, bitemporal workforce composition, governed candidate-to-worker conversion, purpose-bound PII authorization, GET-only People reads, governed People mutation/idempotency API, and the accepted ADR 0001–0003 source expansion integrated by #37. Job Analysis persistence/API and the selection-review packet remain active-PR truth until their unchanged exact heads satisfy fresh gates and merge. diff --git a/manifest.json b/manifest.json index f7b6cf55e..b253f4b8f 100644 --- a/manifest.json +++ b/manifest.json @@ -5,9 +5,9 @@ "files": [ { "path": ".github/workflows/foundation-ci.yml", - "sha256": "b6a4365936b66803a8112f034c77d53d33301a7a798ed4f68746a4f2d8b081d7", - "bytes": 6651, - "lines": 125 + "sha256": "31c6a46cb81513cdaa2a08eed5cb00a57d8b36f15e3c772d230452cdc3329aed", + "bytes": 6974, + "lines": 127 }, { "path": ".gitignore", @@ -29,9 +29,9 @@ }, { "path": "CHANGELOG.md", - "sha256": "f2d2e0b488c0440533effa821808f2f17e37d92f8fb586174c2fdb594f760ca5", - "bytes": 17539, - "lines": 77 + "sha256": "42ef89549edf4aa0d7dd03eece2a386895a2561df06f277761c45f575c4c21e5", + "bytes": 21006, + "lines": 83 }, { "path": "CLAUDE.md", @@ -359,9 +359,9 @@ }, { "path": "scripts/foundation-contract-core.mjs", - "sha256": "9b03efbbdffa60a05f5924e8a61b1cbc3cd75c502df428a5920085e8d0bf3603", - "bytes": 28121, - "lines": 688 + "sha256": "5dfc54d40820dfc45962dcc91367c57baf6b011e68efc5f6e8d59e7e82145b2a", + "bytes": 28182, + "lines": 689 }, { "path": "scripts/foundation-contract.mjs", @@ -465,11 +465,17 @@ "bytes": 14708, "lines": 301 }, + { + "path": "tests/test_workforce_validation_owner_schema_postgres.sh", + "sha256": "29f0cd8a7d9040ff86095b68fa2f3d0ed54ea3777e5a816d4a79eb6ceafb9339", + "bytes": 2949, + "lines": 76 + }, { "path": "tests/validate_repository.py", - "sha256": "091836b2f68600a30b08f7da2cea8b3bef10201a123da720a7369bf10985eec2", - "bytes": 27237, - "lines": 637 + "sha256": "244627252e7392e4dbed98392c132cb86dc8e5ead839a1129298e8d022fcf8eb", + "bytes": 27300, + "lines": 638 } ] } diff --git a/scripts/foundation-contract-core.mjs b/scripts/foundation-contract-core.mjs index 4aacefb3c..ba2d8c00b 100644 --- a/scripts/foundation-contract-core.mjs +++ b/scripts/foundation-contract-core.mjs @@ -85,6 +85,7 @@ export const REQUIRED_FILES = Object.freeze([ 'tests/test_audit_outbox_hardening_postgres.sh', 'tests/test_candidate_worker_conversion_postgres.sh', 'tests/test_validity_study_case_postgres.sh', + 'tests/test_workforce_validation_owner_schema_postgres.sh', 'tests/test_criterion_observation_scope_postgres.sh', 'tests/test_people_mutation_idempotency_postgres.sh', 'tests/test_job_analysis_snapshot_postgres.sh', @@ -685,4 +686,4 @@ export function runCli(rootPath, outputStream = process.stdout, errorStream = pr } errorStream.write(`${JSON.stringify({ status: 'failed', error_count: errors.length, errors }, null, 2)}\n`); return 1; -} +} \ No newline at end of file diff --git a/services/workforce-validation-api/README.md b/services/workforce-validation-api/README.md new file mode 100644 index 000000000..fa1f7666d --- /dev/null +++ b/services/workforce-validation-api/README.md @@ -0,0 +1,166 @@ +# Orgmetra Workforce Validation API + +This package is the canonical application boundary for the `workforce_validation` bounded context. It owns purpose-bound validation-study reads and scientific-evidence corroboration without copying People, Talent Acquisition, Performance Management, Job Architecture, Psychometrics Commons, fast-mlsirm, TEPP, or another bounded context's application tables. + +Foreign domain truth crosses this boundary only through released/versioned contracts, opaque references, immutable evidence digests, and owner ports. Mutable `validity-analysis` source is not a runtime or source dependency of this service. + +## Boundary invariants + +- Keyverse identity is consumed as structurally immutable authenticated identity attributes, never credentials. +- Authorization is evaluated before an owner read. +- Repository capabilities are checked with inert static lookup; inherited Protocol placeholders and executable descriptors are not accepted as owner implementations. +- Tenant/study UUID identities are detached to exact integer payloads and reconstructed at public boundaries so retained UUID aliases cannot rewrite accepted authority. +- Owner evidence is reconstructed into exact tuple-backed records before comparison or projection. +- Returned views are field-minimized corroborating data. Their public constructors fail closed, and a view is not a reusable authorization credential. +- Cross-context SQL, mutable branch dependencies, row-level statistical weights, replicate vectors, protected attributes, and foreign application-table values do not cross this application boundary. + +## Validity-study registry + +`read_validity_study(...)` authorizes the exact tenant/study/purpose/operation/field request, then reads through `ValidityStudyReadPort`. Persisted registry evidence is reconstructed before tenant/study validation and only authorized fields are returned. + +`ValidityStudyView` is a minimized projection. Downstream consequential actions must perform their own purpose-bound authorization and authoritative re-resolution. + +## Calibration auxiliary authority + +`resolve_calibration_auxiliary_authority(...)` corroborates #407's purpose-limited calibration input without copying protected source attributes. It binds auxiliary authority and projection coordinates, scientific-use purpose, released owner contract, authorization receipt/interval, and scientific-use receipt/instant. The governing owner-contract release instant and authorization-receipt release instant are owner-resolved evidence rather than caller coordinates. Both must be timezone-aware; the owner contract may not be released after the authorization receipt, and the authorization receipt must already be released no later than `authorized_from`. This closes the retroactive-authority gap where an immutable authorization receipt created later could otherwise appear to authorize an earlier interval. Caller `used_at` must equal the owner-resolved scientific-use instant, and that instant must fall inside the owner-resolved authorization interval. + +## calibration benchmark authority + +`resolve_calibration_benchmark_authority(...)` corroborates benchmark receipt/version/digest, released benchmark-owner contract, reference/release chronology, and append-only predecessor/successor correction lineage. The owner contract must already be released when the benchmark receipt becomes released evidence; a predecessor is authoritative only on its owner-resolved half-open interval `[released_at, superseded_at)`. When a successor exists, its released instant must equal the predecessor cutover exactly, so two released benchmark receipts cannot overlap in authority and no authority gap can appear between them. + +## Typed calibration-adjustment authority + +`resolve_calibration_adjustment_authority(...)` corroborates the exact released calibration receipt that produced a point-weight artifact. It binds the receipt/evidence version to the exact target-population digest and analysis-window reference, scientific auxiliary-authority reference, auxiliary projection reference/version/digest, scientific-use purpose reference/digest, auxiliary owner-contract reference/version/digest, authorization and scientific-use receipt references/digests plus scientific-use instant, benchmark receipt reference/version/digest, benchmark owner-contract reference/version/digest plus benchmark reference instant, primary method, constraints, artifacts, construction time, and the released application owner contract. `fallback_applied` additionally requires the primary failure reason, immutable fallback rule, and actual fallback algorithm/version/configuration; `converged` rejects fallback-only evidence. Raw auxiliary values, benchmark totals, protected attributes, and row-level weights are excluded. + +The supporting auxiliary-use and benchmark-reference instants are caller-known receipt coordinates committed by the scientific calibration receipt and may not postdate calibration construction. The application owner-contract release instant is canonical owner evidence, not a resolver request coordinate. It must be timezone-aware and no later than the calibration receipt release. A contract may legitimately be released after adjustment construction but before receipt release, while a later contract cannot retroactively authorize an already released calibration receipt. + +The ordinary typed-calibration record also carries an optional owner-resolved `superseded_at`. It is not a caller lookup coordinate and is not projected as reusable downstream authority. Scientific use is valid only on `[released_at, superseded_at)`: historical use before cutover remains reproducible and use at or after cutover fails closed. + +## Calibration support chronology authority + +`resolve_calibration_support_authority(...)` independently corroborates whether the exact auxiliary and benchmark support named by a typed calibration receipt actually existed and was authoritative when calibration was constructed. It binds the calibration receipt to the exact auxiliary authority/projection/purpose/authorization/scientific-use coordinates and the exact benchmark receipt/owner coordinates while resolving release, effective-interval, and benchmark-cutover chronology from immutable owner evidence. + +Auxiliary chronology must satisfy `auxiliary_owner_contract_released_at <= auxiliary_authorization_receipt_released_at <= auxiliary_authorized_from <= auxiliary_scientific_use_at`; when the authorization interval is bounded, scientific use must occur before its exclusive end. The authorization receipt therefore cannot retroactively validate an interval that began before the receipt existed. The scientific-use instant may not postdate calibration construction. Benchmark owner evidence must predate benchmark receipt release, the benchmark receipt must already be released by calibration construction, and a benchmark superseded at or before construction cannot support that calibration. Owner-resolved release/effective/cutover timestamps are deliberately excluded from resolver and owner-read lookup coordinates so callers cannot manufacture favorable chronology. + +## Calibration-adjustment supersession authority + +`resolve_calibration_adjustment_supersession_authority(...)` proves the append-only correction edge behind typed-calibration currentness. An ordinary `superseded_at` alone is not enough to prove which immutable receipt ended the predecessor interval. A correction therefore requires one complete successor calibration receipt reference/digest/evidence-version/release tuple. + +The successor must identify new immutable receipt evidence, remain on the governed v1 contract, be released after its predecessor, and satisfy `successor_released_at == superseded_at`. Successor coordinates are owner-resolved and omitted from the minimized current-receipt view. Durable persistence must make the ordinary calibration projection's cutover and the explicit successor edge agree on the same atomic correction instant; mutable current rows and caller timestamps are not correction authority. + +## Typed nonresponse-adjustment authority + +`resolve_nonresponse_adjustment_authority(...)` corroborates the exact released disposition-aware nonresponse receipt. It binds receipt/evidence version, exact response/disposition receipt reference/version/digest, owner-resolved disposition release time, adjustment population, method/version/configuration, explicit ineligible/unknown/unavailable treatments, input/output weight artifacts, construction time, and released owner contract. The disposition input must already exist by adjustment construction and scientific use cannot precede the typed receipt's release. Response values, source attributes, protected attributes, and row-level weights are excluded. + +The owner-contract release instant and optional exclusive nonresponse cutover are canonical owner evidence rather than caller lookup coordinates. The ordinary resolver enforces `[released_at, superseded_at)`, so a corrected disposition, adjustment population, treatment rule, or weight-artifact transition cannot leave an older receipt apparently current. + +## Nonresponse-adjustment supersession authority + +`resolve_nonresponse_adjustment_supersession_authority(...)` binds the current nonresponse receipt and owner contract to release chronology and, when corrected, requires one complete successor receipt reference/digest/evidence-version/release tuple. The successor must identify new immutable evidence on the governed v1 contract, be released after its predecessor, and satisfy `successor_released_at == superseded_at`. Durable persistence must cross-check the ordinary nonresponse cutover against this explicit edge. + +## Trimming/bounding adjustment authority + +`resolve_trimming_bounding_authority(...)` corroborates the exact released trimming or bounding receipt rather than trusting an adjustment-chain digest alone. It binds the typed receipt reference/digest/evidence version to the governed `weight_trimming_rule` reference/version, immutable rule configuration, exact affected-case occurrence-set digest and positive affected-case count, input/output weight-artifact transition, construction time and released owner-contract tuple. Input and output artifacts may not alias; release cannot precede construction or scientific use. Case identities and row-level weights are excluded from the projection. + +The governing owner-contract release instant and optional exclusive trimming/bounding cutover are owner-resolved evidence and never caller lookup coordinates. The ordinary resolver enforces `[released_at, superseded_at)`. + +## Trimming/bounding supersession authority + +`resolve_trimming_bounding_supersession_authority(...)` supplies the explicit correction edge for typed trimming/bounding receipts. A corrected predecessor requires one complete new successor receipt reference/digest/evidence-version/release tuple, with successor release exactly at the predecessor's `superseded_at`. Successor coordinates remain internal owner evidence. + +## Weight-eligibility authority + +`resolve_weight_eligibility_authority(...)` binds the exact `weight_eligibility_receipt` reference/digest/evidence version to explicit `cross_sectional | longitudinal` scope, governed target-population and reference-duration coordinates, exact eligible-case set, exact point-weight artifact, construction time, released owner-contract tuple, and owner-resolved receipt release time. The optional exclusive cutover is owner-resolved and the resolver enforces `[released_at, superseded_at)`. + +## Weight-eligibility supersession authority + +`resolve_weight_eligibility_supersession_authority(...)` proves which immutable successor ended an eligibility receipt's authority. A correction requires a complete successor receipt reference/digest/evidence version/release instant; it must identify new evidence and be released exactly at the predecessor cutover. The durable adapter must make the ordinary eligibility projection's cutover agree with this graph rather than derive currentness from a mutable row or caller timestamp. + +## Base/design-weight authority + +`resolve_base_weight_authority(...)` corroborates the base/design-weight derivation instead of accepting `base_weight_evidence_digest` as an opaque caller label. It binds an exact released base-weight evidence receipt to source-universe and sampling-design receipt references/versions/digests, their release chronology, the sampled occurrence set, stage-wise selection-probability evidence digest and stage count, base-weight method/version, resulting artifact, construction time, and released owner contract. + +Stage-wise probability values stay with the sampling owner. Source-universe, sampling-design and owner-contract release instants are owner-resolved evidence rather than caller coordinates. Source and sampling evidence must already be released when the base weight is constructed; the owner contract must be released no later than the base-weight evidence receipt. The owner read is keyed by the complete caller-known reproducibility tuple rather than a partial receipt/source/design prefix. The ordinary record also carries an owner-resolved optional `superseded_at` and scientific use is valid only on `[released_at, superseded_at)`. + +## Base/design-weight supersession authority + +`resolve_base_weight_supersession_authority(...)` proves the append-only correction edge behind base/design-weight currentness. A corrected predecessor requires one complete successor base-weight evidence receipt reference/digest/evidence-version/release tuple. The successor must identify new immutable receipt evidence, stay on the governed v1 contract, be released after its predecessor, and satisfy `successor_released_at == superseded_at`. Successor coordinates are owner-resolved and omitted from the minimized current-receipt view. Durable persistence must cross-check the ordinary base-weight projection's cutover and this explicit successor edge at one atomic correction instant. + +## Final analysis-weight authority + +`resolve_final_analysis_weight_authority(...)` corroborates the complete #407 point-estimation lineage. It binds the exact final analysis-weight receipt to the estimand, target population, analysis unit/window/reference duration, eligible and analytic-case sets, owner-resolved source-universe and sampling-design evidence, base-weight method/evidence/artifact, ordered typed adjustment chain, final point-weight artifact, weight-eligibility receipt, analytic-case count, append-only correction lineage, construction/release chronology, and released owner contract. + +The owner read is keyed by the complete caller-known reproducibility tuple. Owner-contract release, final-weight release and supersession instants remain owner-resolved chronology. The ordered adjustment chain is immutable and contiguous; known nonresponse/calibration/raking/poststratification/trimming/bounding/winsorization codes require their specialized receipt kind. Scientific use is valid only on `[released_at, superseded_at)`. + +## Final analysis-weight component binding authority + +`resolve_final_weight_component_binding_authority(...)` closes the #411 locator gap and the #423/#424 admission/completeness gap without redefining the existing v1 final-weight receipt. It is keyed only by the immutable final analysis-weight receipt reference/digest/evidence-version and returns one released owner binding containing the exact base-weight evidence receipt reference/version/digest plus the exact receipt reference/version/digest for every admitted governed adjustment in the final-weight chain. Ungoverned adjustment codes fail closed, so `adjustment_bindings` is one-to-one with the ordered adjustment chain: sequence numbers must be contiguous and one-based, and a hole or a sequence starting after 1 is malformed. + +The binding is its own immutable released owner evidence with reference/digest/version and owner-resolved `[released_at, superseded_at)` chronology. A digest is integrity evidence but is not treated as an implicit reverse-lookup API. Durable persistence must make the final-weight receipt identity select one canonical binding deterministically and fail closed on absence, ambiguity, conflicting receipt identity or non-canonical structure. This companion contract does not copy row-level weights, response values, calibration auxiliary values or foreign application tables; it supplies the owner coordinates required to re-resolve those existing typed authorities. + +## Final analysis-weight supersession authority + +`resolve_final_weight_supersession_authority(...)` preserves predecessor release, exclusive supersession and complete released successor coordinates. The successor must have a new receipt reference and digest, advance correction sequence exactly by one, be released after its predecessor, and be released exactly at the predecessor cutover. Successor coordinates are omitted from the downstream view. + +## Point-weight / variance authority + +`resolve_weight_variance_authority(...)` corroborates released sampling evidence, final analysis-weight receipt, analytic-case occurrence set, weight-eligibility receipt, correction sequence, final point-weight artifact, separate variance-design evidence, variance method/evidence semantics, and released owner contract. A variance receipt cannot alias the point-weight receipt, and approximation evidence cannot be represented as exact. The owner read is keyed by the complete compatibility tuple. + +The binding resolves owner-contract release and optional exclusive supersession from canonical owner evidence and enforces `[released_at, superseded_at)`. This prevents corrected point-weight or variance-design lineage from leaving an older compatibility binding apparently current. + +## Point-weight / variance supersession authority + +`resolve_weight_variance_supersession_authority(...)` proves which immutable `variance_compatibility_authority` identity ended a predecessor binding. A correction requires a complete successor authority reference/evidence-version/release tuple; the successor must use a new authority reference on the governed v1 contract, be released after the predecessor, and satisfy `successor_released_at == superseded_at`. Successor coordinates remain owner-resolved and are omitted from the minimized view. Durable persistence must cross-check the ordinary compatibility projection's `superseded_at` against this explicit successor edge rather than manufacture currentness from a mutable row or caller timestamp. + +## Released validation-result authority + +`resolve_validation_result_authority(...)` binds one immutable validation result to the exact point-weight/variance compatibility receipt, final analysis-weight receipt, separate variance-design receipt, non-authorizing `verification_pending | not_verifiable` state, and released owner contract. Owner-contract release and optional exclusive cutover are canonical owner chronology. Historical reads before cutover remain reproducible; use at or after cutover fails closed. + +## Validation-result supersession authority + +`resolve_validation_result_supersession_authority(...)` proves the released predecessor/successor result edge. A successor must use a new result reference and digest, advance correction sequence exactly by one, be released after its predecessor, and be released exactly at the predecessor cutover. Caller timestamps and mutable result rows do not establish correction authority. + +## Released non-verifiability outcome + +`resolve_validation_result_nonverifiability(...)` represents required analysis-weight, weight/variance-compatibility, or variance-design evidence that is `missing | non_reproducible` without turning lookup failure into scientific GREEN. Missing evidence carries no fabricated reference, digest, or release timestamp. Non-reproducible evidence retains the exact failed reference/digest and owner-resolved release instant; the failed evidence must already have been released when the verification attempt is evaluated. + +The immutable `verification_attempt_reference` and `verification_attempt_digest` are always part of the resolver and owner-read lookup identity. For `non_reproducible`, the exact typed `failed_evidence_reference` and canonical `failed_evidence_digest` are lookup coordinates as well, preventing same-family different failed artifacts from sharing an owner selection prefix; `missing` requires those coordinates absent. Failed-evidence release, verification-attempt release, governing owner-contract release and optional exclusive `superseded_at` remain owner-resolved chronology. The immutable attempt receipt must satisfy `evaluated_at <= verification_attempt_released_at <= released_at`, and the ordinary resolver accepts the released negative outcome only on `[released_at, superseded_at)`. + +## Validation-result non-verifiability supersession authority + +`resolve_validation_result_nonverifiability_supersession_authority(...)` is the v1 correction contract for a predecessor `failure_mode="missing"`. It requires a complete immutable successor verification-attempt reference/digest/release tuple plus an owner-supplied target-result reference/digest and `successor_failed_evidence_kind`. The successor target must equal the predecessor result exactly, the failed-evidence kind must match, the successor attempt must identify new evidence, and its release must equal the predecessor `superseded_at`. This v1 path deliberately rejects `non_reproducible` because its tuple cannot retain the exact artifact whose reproducibility failed. + +`resolve_validation_result_nonverifiability_supersession_v2_authority(...)` supplies that exact-artifact path without weakening v1. It consumes the canonical released `ValidationResultNonVerifiabilityRecord` for a `non_reproducible` predecessor, keeps the failed-evidence reference/digest/release instant as predecessor provenance, and requires any successor attempt to target the exact same result, failed-evidence family, failed-evidence reference, failed-evidence digest, and failed-evidence release chronology. The v2 resolver and owner-read port key the predecessor by exact failed-evidence reference and digest so two artifacts in the same evidence family cannot share an ambiguous correction lookup. The failed-evidence release instant and cutover remain owner-resolved chronology rather than caller lookup coordinates. The v2 successor must use new immutable verification-attempt evidence and be released exactly at the ordinary predecessor's owner-resolved `superseded_at`. An explicit v2 successor is invalid when the ordinary predecessor has no cutover or a different cutover, and omitting successor coordinates is invalid when the ordinary predecessor is already marked superseded. + +Both minimized views omit cutover and successor coordinates. A successor verification attempt only ends the predecessor negative-outcome interval; it does not imply scientific GREEN. Any subsequent released result or negative outcome must satisfy its own governed owner and verification contract. + +## Persistence state + +`services/workforce-validation-api/database/migrations/0001_owner_schema.sql` starts this bounded context's migration history. It creates the `workforce_validation` schema and a deny-default `workforce_validation_role`, revokes public schema access, and intentionally creates or moves no application table yet. + +The schema owner is NOLOGIN and is not a runtime isolation control. PostgreSQL role-level `search_path` defaults are applied at login and are not re-applied by `SET ROLE`. A durable runtime adapter therefore needs a distinct least-privilege runtime role, schema-qualified relations, and explicit function-level `search_path` for any future `SECURITY DEFINER` function. + +Protected foundation migrations still hold validity-study relations in the legacy foundation schema. PR #248 or a verified successor owns forward owner-schema adoption after this application owner reaches normal protected integration. It must preserve valid persistence/FK/RLS/ACL evidence and implement durable released-evidence ports for all **twenty-two** current application-owner families: calibration auxiliary, calibration benchmark, typed calibration adjustment, calibration support chronology, calibration-adjustment supersession, typed nonresponse adjustment, nonresponse-adjustment supersession, trimming/bounding adjustment, trimming/bounding supersession, weight eligibility, weight-eligibility supersession, base/design-weight provenance, base/design-weight supersession, complete final analysis-weight lineage, final-weight typed-component binding, final-weight supersession, point-weight/variance compatibility, point-weight/variance supersession, validation-result binding, validation-result supersession, validation-result non-verifiability, and validation-result non-verifiability supersession. + +The typed-calibration adapter must exact-key the complete target-population/window and auxiliary/benchmark/generating-method/artifact/application-owner tuple. Calibration auxiliary persistence must recover the authorization-receipt release instant from immutable owner evidence and enforce `owner_contract_released_at <= authorization_receipt_released_at <= authorized_from`; it must never manufacture that chronology from a mutable authorization row or caller timestamp. Calibration support persistence must separately bind the exact typed calibration receipt to those auxiliary and benchmark identities, recover release/effective/cutover chronology from owner evidence, enforce authorization release before effective start and scientific use, reject benchmark evidence released after calibration construction, and reject benchmark evidence superseded at or before construction. Calibration, eligibility, nonresponse, trimming/bounding, base-weight and point-weight/variance compatibility ordinary cutovers must agree with their explicit successor graph on one atomic correction instant: `predecessor.superseded_at == successor.released_at`. Calibration-benchmark, final-weight and validation-result correction adapters have the same atomic release-at-cutover invariant. Base-weight and final-analysis-weight persistence must select evidence by their complete caller-known reproducibility tuples. Final-weight component-binding persistence must key only the exact final receipt identity, return exactly one canonical released binding, preserve exact base and every governed adjustment receipt reference-version-digest locator in contiguous one-based sequence, and reject ambiguous digest-only reverse lookup. Non-verifiability persistence must key the exact immutable predecessor verification-attempt reference/digest; for `non_reproducible` it must additionally key the exact failed-evidence reference/digest, while failed-evidence and attempt-release instants remain owner chronology. Missing-evidence correction uses the v1 same-result/same-obligation successor graph. Non-reproducible correction uses v2 and must preserve the same predecessor failed-evidence reference/digest in addition to result, evidence family, verification attempt and owner contract; it must persist/recover the failed-evidence release instant as owner chronology and require the successor target tuple to equal the same exact artifact. V2 must also cross-check the explicit correction cutover against the ordinary predecessor's owner-resolved `superseded_at`; neither side may manufacture or hide the other. Both versions require successor verification-attempt release exactly at the predecessor cutover. No durable adapter may infer currentness from mutable current rows, unrelated result attempts, different-evidence-family attempts, different artifacts in the same family, or caller-supplied timestamps. + +## Test contract + +The canonical Foundation quality workflow still invokes this service's pytest lane from the checkout, including a source-tree `PYTHONPATH` for ordinary unit/import coverage. That invocation is **not** accepted as packaging evidence by itself. + +`tests/test_package_metadata_compatibility.py`, `tests/test_built_wheel_metadata_contract.py`, and `tests/test_built_wheel_record_integrity.py` establish the separate installed-distribution boundary. They remove inherited `PIP_*`, `PYTHONPATH`, and `PYTHONHOME`, disable ambient pip configuration, build the Keyverse and Workforce Validation wheels from the exact checkout with reviewed tooling and no dependency/index acquisition, reject unexpected wheel identities/content/package-data, parse the exact built `.dist-info/METADATA`, and bind built `Name`, `Version`, `Requires-Python`, plus Workforce Validation's mandatory unconditional exact `orgmetra-keyverse-adapter==0.1.0` `Requires-Dist` to reviewed project metadata. Each actual built wheel must also contain exactly one canonical `.dist-info/RECORD` whose rows cover every archived file exactly once, whose non-self entries use correct SHA-256 digests and byte sizes, and whose self-entry leaves hash and size empty. Each wheel is SHA-256-bound before installation; a fresh venv consumes only the local wheelhouse under pip hash-checking mode, then runs `pip check` and proves imports resolve under the isolated prefix. Directly installing Keyverse as a top-level lock entry must never mask a service wheel that dropped or altered its dependency declaration, and an outer artifact digest must never substitute for a truthful installation ledger. + +The Foundation pytest invocation remains: + +```bash +PYTHONPATH=services/workforce-validation-api/src:packages/keyverse-adapter/src \ + COVERAGE_FILE=/tmp/orgmetra-workforce-validation-api.coverage \ + python -m pytest -c services/workforce-validation-api/pyproject.toml \ + services/workforce-validation-api/tests +``` + +`tests/test_workforce_validation_owner_schema_postgres.sh` separately executes the service-local migration against pinned PostgreSQL 16.14 and checks deny-default owner-role/schema behavior, actual `SET ROLE` search-path behavior, PUBLIC privileges, and absence of application relations in the bootstrap schema. + +Scientific-authority tests cover authorization-before-owner-read, static port validation, malformed references/digests/versions/timestamps, exact owner-coordinate matching, UUID detachment/alias attacks, structural immutability, non-public view issuance, calibration-auxiliary owner-contract and authorization-receipt release chronology, benchmark correction chronology, complete typed-calibration context and fallback provenance, calibration-support release/effective/currentness chronology including retroactive-authorization rejection and stale benchmark rejection, typed-calibration owner-resolved currentness and explicit predecessor/successor correction authority with exact release-at-cutover, typed nonresponse currentness and supersession, trimming/bounding currentness and supersession, eligibility currentness and supersession, complete base/design-weight lookup coordinates plus owner-resolved currentness and explicit predecessor/successor correction authority, complete final-analysis-weight lineage/currentness/supersession, deterministic final-weight-to-component receipt binding/currentness including malformed nested and outer record rejection, complete point-weight/variance compatibility/currentness and explicit supersession authority, validation-result currentness/supersession, explicit missing/non-reproducible evidence with exact verification-attempt identity and chronology, ordinary exact failed-artifact lookup for non-reproducible outcomes, v1 same-result/same-failed-evidence-obligation correction for missing predecessors, and v2 exact failed-artifact correction for non-reproducible predecessors including exact failed-artifact lookup identity, ordinary-cutover alignment, hostile mismatched-artifact, chronology, authorization, owner-port and structural-integrity cases. + +These source contracts are not terminal acceptance by themselves. Review admission remains non-authorizing until the exact current head executes with 100% owned statement/branch/docstring/edge evidence, the PostgreSQL owner-schema contract is GREEN, applicable security workflows are terminal, and normal independent review/governance requirements are satisfied. Only protected/released owner evidence may be consumed as durable scientific authority. diff --git a/services/workforce-validation-api/database/migrations/0001_owner_schema.sql b/services/workforce-validation-api/database/migrations/0001_owner_schema.sql new file mode 100644 index 000000000..67a1e3c38 --- /dev/null +++ b/services/workforce-validation-api/database/migrations/0001_owner_schema.sql @@ -0,0 +1,27 @@ +-- Establish the logical PostgreSQL ownership boundary for workforce_validation. +-- This migration intentionally creates no application table. Legacy foundation +-- validity-study tables stay untouched until an explicit forward-only adoption +-- migration can preserve existing foreign-key and acceptance contracts. + +BEGIN; + +CREATE ROLE workforce_validation_role NOLOGIN + NOSUPERUSER + NOCREATEDB + NOCREATEROLE + NOINHERIT + NOREPLICATION + NOBYPASSRLS; + +CREATE SCHEMA workforce_validation AUTHORIZATION workforce_validation_role; +REVOKE ALL ON SCHEMA workforce_validation FROM PUBLIC; + +-- workforce_validation_role is a migration/schema-owner identity only. Runtime +-- principals must not be granted this owner role. PostgreSQL role-level GUC +-- defaults apply at login and are not re-applied by SET ROLE; because this role +-- is NOLOGIN, an ALTER ROLE ... SET search_path entry would not provide runtime +-- isolation. Future runtime adapters must use a distinct least-privilege role, +-- schema-qualified owner relations, and explicit function-level search_path for +-- any SECURITY DEFINER code. + +COMMIT; diff --git a/services/workforce-validation-api/pyproject.toml b/services/workforce-validation-api/pyproject.toml new file mode 100644 index 000000000..ff81b4f6d --- /dev/null +++ b/services/workforce-validation-api/pyproject.toml @@ -0,0 +1,41 @@ +[build-system] +requires = ["setuptools==82.0.1"] +build-backend = "setuptools.build_meta" + +[project] +name = "orgmetra-workforce-validation-api" +version = "0.1.0" +description = "Purpose-bound owner boundary for Orgmetra workforce-validation studies." +readme = "README.md" +requires-python = ">=3.12" +license = "Apache-2.0" +authors = [{ name = "ContextualWisdomLab" }] +dependencies = [ + "orgmetra-keyverse-adapter==0.1.0", +] + +[tool.setuptools] +package-dir = {"" = "src"} + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.package-data] +orgmetra_workforce_validation_api = ["py.typed"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = [ + "--cov=orgmetra_workforce_validation_api", + "--cov-branch", + "--cov-report=term-missing", + "--cov-fail-under=100", +] + +[tool.coverage.run] +branch = true +source = ["orgmetra_workforce_validation_api"] + +[tool.coverage.report] +fail_under = 100 +show_missing = true diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/__init__.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/__init__.py new file mode 100644 index 000000000..818a49d14 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/__init__.py @@ -0,0 +1,363 @@ +"""Canonical workforce-validation application contracts for Orgmetra.""" + +from orgmetra_workforce_validation_api.base_weight_authority import ( + BaseWeightAuthorityIntegrityError, + BaseWeightAuthorityNotFound, + BaseWeightAuthorityReadPort, + BaseWeightAuthorityRecord, + BaseWeightAuthorityView, + resolve_base_weight_authority, +) +from orgmetra_workforce_validation_api.base_weight_supersession_authority import ( + BaseWeightSupersessionAuthorityIntegrityError, + BaseWeightSupersessionAuthorityNotFound, + BaseWeightSupersessionAuthorityReadPort, + BaseWeightSupersessionAuthorityRecord, + BaseWeightSupersessionAuthorityView, + resolve_base_weight_supersession_authority, +) +from orgmetra_workforce_validation_api.benchmark_authority import ( + CalibrationBenchmarkAuthorityIntegrityError, + CalibrationBenchmarkAuthorityNotFound, + CalibrationBenchmarkAuthorityReadPort, + CalibrationBenchmarkAuthorityRecord, + CalibrationBenchmarkAuthorityView, + resolve_calibration_benchmark_authority, +) +from orgmetra_workforce_validation_api.calibration_adjustment_authority import ( + CalibrationAdjustmentAuthorityIntegrityError, + CalibrationAdjustmentAuthorityNotFound, + CalibrationAdjustmentAuthorityReadPort, + CalibrationAdjustmentAuthorityRecord, + CalibrationAdjustmentAuthorityView, + resolve_calibration_adjustment_authority, +) +from orgmetra_workforce_validation_api.calibration_adjustment_supersession_authority import ( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + CalibrationAdjustmentSupersessionAuthorityNotFound, + CalibrationAdjustmentSupersessionAuthorityReadPort, + CalibrationAdjustmentSupersessionAuthorityRecord, + CalibrationAdjustmentSupersessionAuthorityView, + resolve_calibration_adjustment_supersession_authority, +) +from orgmetra_workforce_validation_api.calibration_support_authority import ( + CalibrationSupportAuthorityIntegrityError, + CalibrationSupportAuthorityNotFound, + CalibrationSupportAuthorityReadPort, + CalibrationSupportAuthorityRecord, + CalibrationSupportAuthorityView, + resolve_calibration_support_authority, +) +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityIntegrityError, + FinalAnalysisWeightAuthorityNotFound, + FinalAnalysisWeightAuthorityReadPort, + FinalAnalysisWeightAuthorityRecord, + FinalAnalysisWeightAuthorityView, + FinalWeightAdjustmentCoordinate, + resolve_final_analysis_weight_authority, +) +from orgmetra_workforce_validation_api.final_weight_component_binding_authority import ( + FinalWeightAdjustmentEvidenceBinding, + FinalWeightComponentBindingAuthorityIntegrityError, + FinalWeightComponentBindingAuthorityNotFound, + FinalWeightComponentBindingAuthorityReadPort, + FinalWeightComponentBindingAuthorityRecord, + FinalWeightComponentBindingAuthorityView, + resolve_final_weight_component_binding_authority, +) +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + AdjustmentComponentEvidence, + BaseWeightComponentEvidence, + FinalWeightComponentEvidenceIntegrityError, + FinalWeightComponentEvidenceNotFound, + FinalWeightComponentEvidenceReadPort, + FinalWeightComponentEvidenceResolution, + corroborate_final_weight_component_evidence, +) +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityIntegrityError, + FinalWeightSupersessionAuthorityNotFound, + FinalWeightSupersessionAuthorityReadPort, + FinalWeightSupersessionAuthorityRecord, + FinalWeightSupersessionAuthorityView, + resolve_final_weight_supersession_authority, +) +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityIntegrityError, + NonresponseAdjustmentAuthorityNotFound, + NonresponseAdjustmentAuthorityReadPort, + NonresponseAdjustmentAuthorityRecord, + NonresponseAdjustmentAuthorityView, + resolve_nonresponse_adjustment_authority, +) +from orgmetra_workforce_validation_api.nonresponse_adjustment_supersession_authority import ( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + NonresponseAdjustmentSupersessionAuthorityNotFound, + NonresponseAdjustmentSupersessionAuthorityReadPort, + NonresponseAdjustmentSupersessionAuthorityRecord, + NonresponseAdjustmentSupersessionAuthorityView, + resolve_nonresponse_adjustment_supersession_authority, +) +from orgmetra_workforce_validation_api.registry import ( + ValidationPrincipal, + ValidityStudyIntegrityError, + ValidityStudyNotFound, + ValidityStudyReadPort, + ValidityStudyRecord, + ValidityStudyView, + read_validity_study, +) +from orgmetra_workforce_validation_api.result_authority import ( + ValidationResultAuthorityIntegrityError, + ValidationResultAuthorityNotFound, + ValidationResultAuthorityReadPort, + ValidationResultAuthorityRecord, + ValidationResultAuthorityView, + resolve_validation_result_authority, +) +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityIntegrityError, + ValidationResultNonVerifiabilityNotFound, + ValidationResultNonVerifiabilityReadPort, + ValidationResultNonVerifiabilityRecord, + ValidationResultNonVerifiabilityView, + resolve_validation_result_nonverifiability, +) +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionAuthorityNotFound, + ValidationResultNonVerifiabilitySupersessionAuthorityReadPort, + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + ValidationResultNonVerifiabilitySupersessionAuthorityView, + resolve_validation_result_nonverifiability_supersession_authority, +) +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionV2AuthorityNotFound, + ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort, + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord, + ValidationResultNonVerifiabilitySupersessionV2AuthorityView, + resolve_validation_result_nonverifiability_supersession_v2_authority, +) +from orgmetra_workforce_validation_api.result_supersession_authority import ( + ValidationResultSupersessionAuthorityIntegrityError, + ValidationResultSupersessionAuthorityNotFound, + ValidationResultSupersessionAuthorityReadPort, + ValidationResultSupersessionAuthorityRecord, + ValidationResultSupersessionAuthorityView, + resolve_validation_result_supersession_authority, +) +from orgmetra_workforce_validation_api.scientific_authority import ( + CalibrationAuxiliaryAuthorityIntegrityError, + CalibrationAuxiliaryAuthorityNotFound, + CalibrationAuxiliaryAuthorityReadPort, + CalibrationAuxiliaryAuthorityRecord, + CalibrationAuxiliaryAuthorityView, + resolve_calibration_auxiliary_authority, +) +from orgmetra_workforce_validation_api.trimming_bounding_authority import ( + TrimmingBoundingAuthorityIntegrityError, + TrimmingBoundingAuthorityNotFound, + TrimmingBoundingAuthorityReadPort, + TrimmingBoundingAuthorityRecord, + TrimmingBoundingAuthorityView, + resolve_trimming_bounding_authority, +) +from orgmetra_workforce_validation_api.trimming_bounding_supersession_authority import ( + TrimmingBoundingSupersessionAuthorityIntegrityError, + TrimmingBoundingSupersessionAuthorityNotFound, + TrimmingBoundingSupersessionAuthorityReadPort, + TrimmingBoundingSupersessionAuthorityRecord, + TrimmingBoundingSupersessionAuthorityView, + resolve_trimming_bounding_supersession_authority, +) +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityIntegrityError, + WeightVarianceAuthorityNotFound, + WeightVarianceAuthorityReadPort, + WeightVarianceAuthorityRecord, + WeightVarianceAuthorityView, + resolve_weight_variance_authority, +) +from orgmetra_workforce_validation_api.weight_variance_supersession_authority import ( + WeightVarianceSupersessionAuthorityIntegrityError, + WeightVarianceSupersessionAuthorityNotFound, + WeightVarianceSupersessionAuthorityReadPort, + WeightVarianceSupersessionAuthorityRecord, + WeightVarianceSupersessionAuthorityView, + resolve_weight_variance_supersession_authority, +) +from orgmetra_workforce_validation_api.weight_eligibility_authority import ( + WeightEligibilityAuthorityIntegrityError, + WeightEligibilityAuthorityNotFound, + WeightEligibilityAuthorityReadPort, + WeightEligibilityAuthorityRecord, + WeightEligibilityAuthorityView, + resolve_weight_eligibility_authority, +) +from orgmetra_workforce_validation_api.weight_eligibility_supersession_authority import ( + WeightEligibilitySupersessionAuthorityIntegrityError, + WeightEligibilitySupersessionAuthorityNotFound, + WeightEligibilitySupersessionAuthorityReadPort, + WeightEligibilitySupersessionAuthorityRecord, + WeightEligibilitySupersessionAuthorityView, + resolve_weight_eligibility_supersession_authority, +) + +__all__ = [ + "AdjustmentComponentEvidence", + "BaseWeightAuthorityIntegrityError", + "BaseWeightAuthorityNotFound", + "BaseWeightAuthorityReadPort", + "BaseWeightAuthorityRecord", + "BaseWeightAuthorityView", + "BaseWeightComponentEvidence", + "BaseWeightSupersessionAuthorityIntegrityError", + "BaseWeightSupersessionAuthorityNotFound", + "BaseWeightSupersessionAuthorityReadPort", + "BaseWeightSupersessionAuthorityRecord", + "BaseWeightSupersessionAuthorityView", + "CalibrationAdjustmentAuthorityIntegrityError", + "CalibrationAdjustmentAuthorityNotFound", + "CalibrationAdjustmentAuthorityReadPort", + "CalibrationAdjustmentAuthorityRecord", + "CalibrationAdjustmentAuthorityView", + "CalibrationAdjustmentSupersessionAuthorityIntegrityError", + "CalibrationAdjustmentSupersessionAuthorityNotFound", + "CalibrationAdjustmentSupersessionAuthorityReadPort", + "CalibrationAdjustmentSupersessionAuthorityRecord", + "CalibrationAdjustmentSupersessionAuthorityView", + "CalibrationAuxiliaryAuthorityIntegrityError", + "CalibrationAuxiliaryAuthorityNotFound", + "CalibrationAuxiliaryAuthorityReadPort", + "CalibrationAuxiliaryAuthorityRecord", + "CalibrationAuxiliaryAuthorityView", + "CalibrationBenchmarkAuthorityIntegrityError", + "CalibrationBenchmarkAuthorityNotFound", + "CalibrationBenchmarkAuthorityReadPort", + "CalibrationBenchmarkAuthorityRecord", + "CalibrationBenchmarkAuthorityView", + "CalibrationSupportAuthorityIntegrityError", + "CalibrationSupportAuthorityNotFound", + "CalibrationSupportAuthorityReadPort", + "CalibrationSupportAuthorityRecord", + "CalibrationSupportAuthorityView", + "FinalAnalysisWeightAuthorityIntegrityError", + "FinalAnalysisWeightAuthorityNotFound", + "FinalAnalysisWeightAuthorityReadPort", + "FinalAnalysisWeightAuthorityRecord", + "FinalAnalysisWeightAuthorityView", + "FinalWeightAdjustmentCoordinate", + "FinalWeightAdjustmentEvidenceBinding", + "FinalWeightComponentBindingAuthorityIntegrityError", + "FinalWeightComponentBindingAuthorityNotFound", + "FinalWeightComponentBindingAuthorityReadPort", + "FinalWeightComponentBindingAuthorityRecord", + "FinalWeightComponentBindingAuthorityView", + "FinalWeightComponentEvidenceIntegrityError", + "FinalWeightComponentEvidenceNotFound", + "FinalWeightComponentEvidenceReadPort", + "FinalWeightComponentEvidenceResolution", + "FinalWeightSupersessionAuthorityIntegrityError", + "FinalWeightSupersessionAuthorityNotFound", + "FinalWeightSupersessionAuthorityReadPort", + "FinalWeightSupersessionAuthorityRecord", + "FinalWeightSupersessionAuthorityView", + "NonresponseAdjustmentAuthorityIntegrityError", + "NonresponseAdjustmentAuthorityNotFound", + "NonresponseAdjustmentAuthorityReadPort", + "NonresponseAdjustmentAuthorityRecord", + "NonresponseAdjustmentAuthorityView", + "NonresponseAdjustmentSupersessionAuthorityIntegrityError", + "NonresponseAdjustmentSupersessionAuthorityNotFound", + "NonresponseAdjustmentSupersessionAuthorityReadPort", + "NonresponseAdjustmentSupersessionAuthorityRecord", + "NonresponseAdjustmentSupersessionAuthorityView", + "TrimmingBoundingAuthorityIntegrityError", + "TrimmingBoundingAuthorityNotFound", + "TrimmingBoundingAuthorityReadPort", + "TrimmingBoundingAuthorityRecord", + "TrimmingBoundingAuthorityView", + "TrimmingBoundingSupersessionAuthorityIntegrityError", + "TrimmingBoundingSupersessionAuthorityNotFound", + "TrimmingBoundingSupersessionAuthorityReadPort", + "TrimmingBoundingSupersessionAuthorityRecord", + "TrimmingBoundingSupersessionAuthorityView", + "ValidationPrincipal", + "ValidationResultAuthorityIntegrityError", + "ValidationResultAuthorityNotFound", + "ValidationResultAuthorityReadPort", + "ValidationResultAuthorityRecord", + "ValidationResultAuthorityView", + "ValidationResultNonVerifiabilityIntegrityError", + "ValidationResultNonVerifiabilityNotFound", + "ValidationResultNonVerifiabilityReadPort", + "ValidationResultNonVerifiabilityRecord", + "ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError", + "ValidationResultNonVerifiabilitySupersessionAuthorityNotFound", + "ValidationResultNonVerifiabilitySupersessionAuthorityReadPort", + "ValidationResultNonVerifiabilitySupersessionAuthorityRecord", + "ValidationResultNonVerifiabilitySupersessionAuthorityView", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityNotFound", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityView", + "ValidationResultNonVerifiabilityView", + "ValidationResultSupersessionAuthorityIntegrityError", + "ValidationResultSupersessionAuthorityNotFound", + "ValidationResultSupersessionAuthorityReadPort", + "ValidationResultSupersessionAuthorityRecord", + "ValidationResultSupersessionAuthorityView", + "ValidityStudyIntegrityError", + "ValidityStudyNotFound", + "ValidityStudyReadPort", + "ValidityStudyRecord", + "ValidityStudyView", + "WeightEligibilityAuthorityIntegrityError", + "WeightEligibilityAuthorityNotFound", + "WeightEligibilityAuthorityReadPort", + "WeightEligibilityAuthorityRecord", + "WeightEligibilityAuthorityView", + "WeightEligibilitySupersessionAuthorityIntegrityError", + "WeightEligibilitySupersessionAuthorityNotFound", + "WeightEligibilitySupersessionAuthorityReadPort", + "WeightEligibilitySupersessionAuthorityRecord", + "WeightEligibilitySupersessionAuthorityView", + "WeightVarianceAuthorityIntegrityError", + "WeightVarianceAuthorityNotFound", + "WeightVarianceAuthorityReadPort", + "WeightVarianceAuthorityRecord", + "WeightVarianceAuthorityView", + "WeightVarianceSupersessionAuthorityIntegrityError", + "WeightVarianceSupersessionAuthorityNotFound", + "WeightVarianceSupersessionAuthorityReadPort", + "WeightVarianceSupersessionAuthorityRecord", + "WeightVarianceSupersessionAuthorityView", + "corroborate_final_weight_component_evidence", + "read_validity_study", + "resolve_base_weight_authority", + "resolve_base_weight_supersession_authority", + "resolve_calibration_adjustment_authority", + "resolve_calibration_adjustment_supersession_authority", + "resolve_calibration_auxiliary_authority", + "resolve_calibration_benchmark_authority", + "resolve_calibration_support_authority", + "resolve_final_analysis_weight_authority", + "resolve_final_weight_component_binding_authority", + "resolve_final_weight_supersession_authority", + "resolve_nonresponse_adjustment_authority", + "resolve_nonresponse_adjustment_supersession_authority", + "resolve_trimming_bounding_authority", + "resolve_trimming_bounding_supersession_authority", + "resolve_validation_result_authority", + "resolve_validation_result_nonverifiability", + "resolve_validation_result_nonverifiability_supersession_authority", + "resolve_validation_result_nonverifiability_supersession_v2_authority", + "resolve_validation_result_supersession_authority", + "resolve_weight_eligibility_authority", + "resolve_weight_eligibility_supersession_authority", + "resolve_weight_variance_authority", + "resolve_weight_variance_supersession_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/base_weight_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/base_weight_authority.py new file mode 100644 index 000000000..580a1fd70 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/base_weight_authority.py @@ -0,0 +1,654 @@ +"""Corroborate released base/design-weight evidence without copying row-level weights. + +This boundary keeps stage-wise inclusion-probability evidence with its sampling +owner. Workforce validation receives only released references, versions, digests, +set identity, method identity, and the resulting base-weight artifact needed to +reproduce the scientific weight lineage. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "base_weight_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "evidence_version", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "source_universe_released_at", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "sampling_design_released_at", + "sampled_occurrence_set_digest", + "selection_probability_set_digest", + "selection_stage_count", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + +_OWNER_RESOLVED_RELEASE_FIELDS = frozenset( + { + "source_universe_released_at", + "sampling_design_released_at", + "owner_contract_released_at", + } +) +class BaseWeightAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the base weight.""" + + +class BaseWeightAuthorityIntegrityError(RuntimeError): + """Indicate that released owner evidence cannot corroborate the requested base weight.""" + + +class BaseWeightAuthorityRecord(tuple): + """Immutable owner projection for one released base/design-weight construction.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + source_universe_released_at: datetime, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + sampling_design_released_at: datetime, + sampled_occurrence_set_digest: str, + selection_probability_set_digest: str, + selection_stage_count: int, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> BaseWeightAuthorityRecord: + """Validate the minimum released provenance needed to reproduce a base weight.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "base_weight_evidence_receipt_reference", + base_weight_evidence_receipt_reference, + "base_weight_evidence_receipt", + ) + receipt_digest = _require_digest( + "base_weight_evidence_receipt_digest", base_weight_evidence_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + source_ref = _require_reference( + "source_universe_receipt_reference", + source_universe_receipt_reference, + "source_universe_receipt", + ) + source_version = _require_positive_integer( + "source_universe_receipt_version", source_universe_receipt_version + ) + source_digest = _require_digest( + "source_universe_receipt_digest", source_universe_receipt_digest + ) + source_released = _require_aware_datetime( + "source_universe_released_at", source_universe_released_at + ) + sampling_ref = _require_reference( + "sampling_design_receipt_reference", + sampling_design_receipt_reference, + "sampling_design_receipt", + ) + sampling_version = _require_positive_integer( + "sampling_design_receipt_version", sampling_design_receipt_version + ) + sampling_digest = _require_digest( + "sampling_design_receipt_digest", sampling_design_receipt_digest + ) + sampling_released = _require_aware_datetime( + "sampling_design_released_at", sampling_design_released_at + ) + sampled_digest = _require_digest( + "sampled_occurrence_set_digest", sampled_occurrence_set_digest + ) + probability_digest = _require_digest( + "selection_probability_set_digest", selection_probability_set_digest + ) + stage_count = _require_positive_integer("selection_stage_count", selection_stage_count) + method_code = _require_code("base_weight_method_code", base_weight_method_code) + method_version = _require_positive_integer( + "base_weight_method_version", base_weight_method_version + ) + artifact_digest = _require_digest( + "base_weight_artifact_digest", base_weight_artifact_digest + ) + constructed = _require_aware_datetime("constructed_at", constructed_at) + if source_released > constructed: + raise ValueError("source_universe_released_at cannot be later than constructed_at.") + if sampling_released > constructed: + raise ValueError("sampling_design_released_at cannot be later than constructed_at.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + contract_released_at = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if release_instant < constructed: + raise ValueError("released_at cannot precede constructed_at.") + if contract_released_at > release_instant: + raise ValueError( + "owner contract must be released no later than base-weight evidence receipt." + ) + cutover = None + if superseded_at is not None: + cutover = _require_aware_datetime("superseded_at", superseded_at) + if cutover <= release_instant: + raise ValueError("superseded_at must be later than released_at.") + + fields: tuple[tuple[str, object], ...] = ( + ("base_weight_artifact_digest", artifact_digest), + ("base_weight_evidence_receipt_digest", receipt_digest), + ("base_weight_evidence_receipt_reference", receipt_ref), + ("base_weight_method_code", method_code), + ("base_weight_method_version", method_version), + ("constructed_at", constructed), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", contract_released_at), + ("owner_contract_version", owner_version), + ("sampled_occurrence_set_digest", sampled_digest), + ("sampling_design_receipt_digest", sampling_digest), + ("sampling_design_receipt_reference", sampling_ref), + ("sampling_design_receipt_version", sampling_version), + ("sampling_design_released_at", sampling_released), + ("selection_probability_set_digest", probability_digest), + ("selection_stage_count", stage_count), + ("source_universe_receipt_digest", source_digest), + ("source_universe_receipt_reference", source_ref), + ("source_universe_receipt_version", source_version), + ("source_universe_released_at", source_released), + ) + return tuple.__new__( + cls, (tenant_identity, study_identity, fields, release_instant, cutover) + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable, value-minimized base-weight provenance.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when the base-weight evidence became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this base-weight receipt's authority interval.""" + return self[4] + + +class BaseWeightAuthorityView: + """Sealed field-minimized base-weight evidence issued only after authorization. + + The public constructor is deliberately non-issuing. Raw exact-runtime + allocations remain unusable because each public property verifies the private + resolver seal before exposing detached projection state. Consequential actions + must still re-authorize and re-resolve owner truth rather than trusting a view. + """ + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> BaseWeightAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "BaseWeightAuthorityView is issued only by resolve_base_weight_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("BaseWeightAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("BaseWeightAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations that were not sealed by the resolver.""" + _require_base_weight_authority_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return released base-weight provenance without row-level probabilities.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class BaseWeightAuthorityReadPort(Protocol): + """Owner read contract for one released base/design-weight receipt.""" + + def read_base_weight_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + sampled_occurrence_set_digest: str, + selection_probability_set_digest: str, + selection_stage_count: int, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> BaseWeightAuthorityRecord | None: + """Return matching released base-weight evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + BaseWeightAuthorityReadPort, "read_base_weight_authority" +) + + +def _resolve_base_weight_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + sampled_occurrence_set_digest: str, + selection_probability_set_digest: str, + selection_stage_count: int, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: BaseWeightAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and corroborate base-weight evidence into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static(type(read_port), "read_base_weight_authority", None) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError("read_port must expose a statically callable read_base_weight_authority.") + + requested = BaseWeightAuthorityRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + base_weight_evidence_receipt_reference=base_weight_evidence_receipt_reference, + base_weight_evidence_receipt_digest=base_weight_evidence_receipt_digest, + evidence_version=evidence_version, + source_universe_receipt_reference=source_universe_receipt_reference, + source_universe_receipt_version=source_universe_receipt_version, + source_universe_receipt_digest=source_universe_receipt_digest, + source_universe_released_at=constructed_at, + sampling_design_receipt_reference=sampling_design_receipt_reference, + sampling_design_receipt_version=sampling_design_receipt_version, + sampling_design_receipt_digest=sampling_design_receipt_digest, + sampling_design_released_at=constructed_at, + sampled_occurrence_set_digest=sampled_occurrence_set_digest, + selection_probability_set_digest=selection_probability_set_digest, + selection_stage_count=selection_stage_count, + base_weight_method_code=base_weight_method_code, + base_weight_method_version=base_weight_method_version, + base_weight_artifact_digest=base_weight_artifact_digest, + constructed_at=constructed_at, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + owner_contract_released_at=constructed_at, + released_at=constructed_at, + superseded_at=None, + ) + tenant_id = requested.tenant_record_id + study_id = requested.validity_study_id + requested_values = dict(requested.fields) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=requested.tenant_record_id, + validity_study_id=requested.validity_study_id, + base_weight_evidence_receipt_reference=requested_values[ + "base_weight_evidence_receipt_reference" + ], + base_weight_evidence_receipt_digest=requested_values[ + "base_weight_evidence_receipt_digest" + ], + evidence_version=requested_values["evidence_version"], + source_universe_receipt_reference=requested_values[ + "source_universe_receipt_reference" + ], + source_universe_receipt_version=requested_values[ + "source_universe_receipt_version" + ], + source_universe_receipt_digest=requested_values[ + "source_universe_receipt_digest" + ], + sampling_design_receipt_reference=requested_values[ + "sampling_design_receipt_reference" + ], + sampling_design_receipt_version=requested_values[ + "sampling_design_receipt_version" + ], + sampling_design_receipt_digest=requested_values[ + "sampling_design_receipt_digest" + ], + sampled_occurrence_set_digest=requested_values["sampled_occurrence_set_digest"], + selection_probability_set_digest=requested_values[ + "selection_probability_set_digest" + ], + selection_stage_count=requested_values["selection_stage_count"], + base_weight_method_code=requested_values["base_weight_method_code"], + base_weight_method_version=requested_values["base_weight_method_version"], + base_weight_artifact_digest=requested_values["base_weight_artifact_digest"], + constructed_at=requested_values["constructed_at"], + owner_contract_reference=requested_values["owner_contract_reference"], + owner_contract_version=requested_values["owner_contract_version"], + owner_contract_digest=requested_values["owner_contract_digest"], + ) + if persisted is None: + raise BaseWeightAuthorityNotFound(str(study_id)) + if type(persisted) is not BaseWeightAuthorityRecord: + raise BaseWeightAuthorityIntegrityError( + "owner port returned non-canonical base-weight authority evidence" + ) + + try: + record = BaseWeightAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise BaseWeightAuthorityIntegrityError( + "owner port returned structurally invalid base-weight authority evidence" + ) from exc + if record != persisted: + raise BaseWeightAuthorityIntegrityError( + "owner port returned non-canonical base-weight authority structure" + ) + + record_values = dict(record.fields) + requested_match = tuple( + (field_name, field_value) + for field_name, field_value in requested.fields + if field_name not in _OWNER_RESOLVED_RELEASE_FIELDS + ) + record_match = tuple( + (field_name, record_values[field_name]) + for field_name, _ in requested_match + ) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", requested.tenant_record_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", requested.validity_study_id) + or record_match != requested_match + ): + raise BaseWeightAuthorityIntegrityError( + "released base-weight authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise BaseWeightAuthorityIntegrityError( + "base-weight authority cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise BaseWeightAuthorityIntegrityError( + "base-weight authority is superseded for this scientific-use instant" + ) + + values = dict(record.fields) + values["released_at"] = record.released_at + values["superseded_at"] = record.superseded_at + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_base_weight_authority_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: BaseWeightAuthorityView) -> None: + """Verify one base-weight view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise BaseWeightAuthorityIntegrityError( + "base-weight authority view was not issued by resolve_base_weight_authority" + ) from exc + if marker is not issuance_marker: + raise BaseWeightAuthorityIntegrityError( + "base-weight authority view was not issued by resolve_base_weight_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + sampled_occurrence_set_digest: str, + selection_probability_set_digest: str, + selection_stage_count: int, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: BaseWeightAuthorityReadPort, + ) -> BaseWeightAuthorityView: + """Authorize then corroborate released stage-wise base-weight evidence.""" + tenant_identity, study_identity, fields = _resolve_base_weight_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + base_weight_evidence_receipt_reference=base_weight_evidence_receipt_reference, + base_weight_evidence_receipt_digest=base_weight_evidence_receipt_digest, + evidence_version=evidence_version, + source_universe_receipt_reference=source_universe_receipt_reference, + source_universe_receipt_version=source_universe_receipt_version, + source_universe_receipt_digest=source_universe_receipt_digest, + sampling_design_receipt_reference=sampling_design_receipt_reference, + sampling_design_receipt_version=sampling_design_receipt_version, + sampling_design_receipt_digest=sampling_design_receipt_digest, + sampled_occurrence_set_digest=sampled_occurrence_set_digest, + selection_probability_set_digest=selection_probability_set_digest, + selection_stage_count=selection_stage_count, + base_weight_method_code=base_weight_method_code, + base_weight_method_version=base_weight_method_version, + base_weight_artifact_digest=base_weight_artifact_digest, + constructed_at=constructed_at, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + view = object.__new__(BaseWeightAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_base_weight_authority_view_issued, + resolve_base_weight_authority, +) = _build_base_weight_authority_view_runtime() +del _build_base_weight_authority_view_runtime + + +__all__ = [ + "BaseWeightAuthorityIntegrityError", + "BaseWeightAuthorityNotFound", + "BaseWeightAuthorityReadPort", + "BaseWeightAuthorityRecord", + "BaseWeightAuthorityView", + "resolve_base_weight_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/base_weight_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/base_weight_supersession_authority.py new file mode 100644 index 000000000..ee04e7867 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/base_weight_supersession_authority.py @@ -0,0 +1,580 @@ +"""Corroborate append-only base/design-weight correction authority. + +The ordinary base-weight projection proves which released sampling evidence +produced a base-weight artifact. This boundary proves the half-open authority +interval for that immutable receipt and, when corrected, the exact released +successor that ends the interval. Successor chronology is owner-resolved and is +never accepted as a caller-selected lookup coordinate. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "base_weight_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_base_weight_evidence_receipt_reference", + "successor_base_weight_evidence_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class BaseWeightSupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the base-weight receipt.""" + + +class BaseWeightSupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released base-weight correction evidence cannot authorize use.""" + + +class BaseWeightSupersessionAuthorityRecord(tuple): + """Immutable owner projection for one base-weight receipt authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_base_weight_evidence_receipt_reference: str | None = None, + successor_base_weight_evidence_receipt_digest: str | None = None, + successor_evidence_version: int | None = None, + successor_released_at: datetime | None = None, + ) -> BaseWeightSupersessionAuthorityRecord: + """Validate one released predecessor and its optional atomic successor edge.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "base_weight_evidence_receipt_reference", + base_weight_evidence_receipt_reference, + "base_weight_evidence_receipt", + ) + receipt_digest = _require_digest( + "base_weight_evidence_receipt_digest", base_weight_evidence_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release > release_instant: + raise ValueError( + "owner contract must be released no later than base-weight receipt." + ) + + successor_values = ( + superseded_at, + successor_base_weight_evidence_receipt_reference, + successor_base_weight_evidence_receipt_digest, + successor_evidence_version, + successor_released_at, + ) + if all(value is None for value in successor_values): + cutover = None + successor_ref = None + successor_digest = None + successor_version = None + successor_release = None + elif any(value is None for value in successor_values): + raise ValueError( + "base-weight supersession requires cutover and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_base_weight_evidence_receipt_reference", + successor_base_weight_evidence_receipt_reference, + "base_weight_evidence_receipt", + ) + successor_digest = _require_digest( + "successor_base_weight_evidence_receipt_digest", + successor_base_weight_evidence_receipt_digest, + ) + successor_version = _require_positive_integer( + "successor_evidence_version", successor_evidence_version + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover <= release_instant: + raise ValueError("superseded_at must be later than base-weight receipt release.") + if successor_ref == receipt_ref: + raise ValueError("successor base-weight receipt must have a new reference.") + if successor_digest == receipt_digest: + raise ValueError("successor base-weight receipt must identify new evidence.") + if successor_version != 1: + raise ValueError("successor_evidence_version must remain 1.") + if successor_release <= release_instant: + raise ValueError( + "successor base-weight receipt must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor base-weight receipt must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("base_weight_evidence_receipt_digest", receipt_digest), + ("base_weight_evidence_receipt_reference", receipt_ref), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", owner_release), + ("owner_contract_version", owner_version), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_base_weight_evidence_receipt_digest", successor_digest), + ("successor_base_weight_evidence_receipt_reference", successor_ref), + ("successor_evidence_version", successor_version), + ("successor_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-receipt authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this base-weight receipt became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this receipt's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class BaseWeightSupersessionAuthorityView: + """Sealed current-receipt authority issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> BaseWeightSupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "BaseWeightSupersessionAuthorityView is issued only by " + "resolve_base_weight_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("BaseWeightSupersessionAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("BaseWeightSupersessionAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_base_weight_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return current receipt authority without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class BaseWeightSupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released base-weight correction state.""" + + def read_base_weight_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> BaseWeightSupersessionAuthorityRecord | None: + """Return matching released base-weight supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + BaseWeightSupersessionAuthorityReadPort, + "read_base_weight_supersession_authority", +) + + +def _resolve_base_weight_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: BaseWeightSupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve base-weight supersession into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_base_weight_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable read_base_weight_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + receipt_ref = _require_reference( + "base_weight_evidence_receipt_reference", + base_weight_evidence_receipt_reference, + "base_weight_evidence_receipt", + ) + receipt_digest = _require_digest( + "base_weight_evidence_receipt_digest", base_weight_evidence_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + base_weight_evidence_receipt_reference=receipt_ref, + base_weight_evidence_receipt_digest=receipt_digest, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise BaseWeightSupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not BaseWeightSupersessionAuthorityRecord: + raise BaseWeightSupersessionAuthorityIntegrityError( + "owner port returned non-canonical base-weight supersession evidence" + ) + + try: + persisted_fields = dict(persisted.fields) + persisted_successor = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = BaseWeightSupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + base_weight_evidence_receipt_reference=persisted_fields[ + "base_weight_evidence_receipt_reference" + ], + base_weight_evidence_receipt_digest=persisted_fields[ + "base_weight_evidence_receipt_digest" + ], + evidence_version=persisted_fields["evidence_version"], + owner_contract_reference=persisted_fields["owner_contract_reference"], + owner_contract_version=persisted_fields["owner_contract_version"], + owner_contract_digest=persisted_fields["owner_contract_digest"], + owner_contract_released_at=persisted_fields["owner_contract_released_at"], + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_base_weight_evidence_receipt_reference=( + None + if persisted_successor is None + else persisted_successor[ + "successor_base_weight_evidence_receipt_reference" + ] + ), + successor_base_weight_evidence_receipt_digest=( + None + if persisted_successor is None + else persisted_successor[ + "successor_base_weight_evidence_receipt_digest" + ] + ), + successor_evidence_version=( + None + if persisted_successor is None + else persisted_successor["successor_evidence_version"] + ), + successor_released_at=( + None + if persisted_successor is None + else persisted_successor["successor_released_at"] + ), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise BaseWeightSupersessionAuthorityIntegrityError( + "owner port returned malformed base-weight supersession evidence" + ) from exc + if record != persisted: + raise BaseWeightSupersessionAuthorityIntegrityError( + "owner port returned non-canonical base-weight supersession structure" + ) + + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["base_weight_evidence_receipt_reference"] != receipt_ref + or record_values["base_weight_evidence_receipt_digest"] != receipt_digest + or record_values["evidence_version"] != version + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise BaseWeightSupersessionAuthorityIntegrityError( + "released base-weight supersession authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise BaseWeightSupersessionAuthorityIntegrityError( + "base-weight receipt must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise BaseWeightSupersessionAuthorityIntegrityError( + "base-weight receipt is superseded for this scientific-use instant" + ) + + fields = record.fields + ( + ("released_at", record.released_at), + ("superseded_at", record.superseded_at), + ) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_base_weight_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: BaseWeightSupersessionAuthorityView) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise BaseWeightSupersessionAuthorityIntegrityError( + "base-weight supersession view was not issued by " + "resolve_base_weight_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise BaseWeightSupersessionAuthorityIntegrityError( + "base-weight supersession view was not issued by " + "resolve_base_weight_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: BaseWeightSupersessionAuthorityReadPort, + ) -> BaseWeightSupersessionAuthorityView: + """Authorize then resolve the base-weight receipt authority interval.""" + tenant_identity, study_identity, fields = ( + _resolve_base_weight_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + base_weight_evidence_receipt_reference=( + base_weight_evidence_receipt_reference + ), + base_weight_evidence_receipt_digest=base_weight_evidence_receipt_digest, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(BaseWeightSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_base_weight_supersession_view_issued, + resolve_base_weight_supersession_authority, +) = _build_base_weight_supersession_view_runtime() +del _build_base_weight_supersession_view_runtime + + +__all__ = [ + "BaseWeightSupersessionAuthorityIntegrityError", + "BaseWeightSupersessionAuthorityNotFound", + "BaseWeightSupersessionAuthorityReadPort", + "BaseWeightSupersessionAuthorityRecord", + "BaseWeightSupersessionAuthorityView", + "resolve_base_weight_supersession_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/benchmark_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/benchmark_authority.py new file mode 100644 index 000000000..6dc695dde --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/benchmark_authority.py @@ -0,0 +1,663 @@ +"""Corroborate released calibration benchmark authority through its owner port. + +This application boundary verifies the immutable benchmark coordinates carried by +scientific weighting evidence without copying benchmark values or reading another +bounded context's tables. Durable PostgreSQL/release resolution remains a child +persistence responsibility after this owner service integrates. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +import re +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) + +_DIGEST_PATTERN = re.compile(r"^[0-9a-f]{64}$") +_REFERENCE_PATTERN = re.compile(r"^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$") +_RESOURCE_KIND = "calibration_benchmark_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_receipt_digest", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_reference_at", + "benchmark_receipt_released_at", + "owner_contract_released_at", + } +) + + +class CalibrationBenchmarkAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the benchmark tuple.""" + + +class CalibrationBenchmarkAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence cannot corroborate the requested benchmark tuple.""" + + +def _require_reference(field_name: str, value: object, namespace: str) -> str: + """Require one exact opaque namespaced reference without benchmark values.""" + if ( + type(value) is not str + or _REFERENCE_PATTERN.fullmatch(value) is None + or value.partition(":")[0] != namespace + ): + raise ValueError(f"{field_name} must be an exact {namespace}: opaque reference.") + return value + + +def _require_digest(field_name: str, value: object) -> str: + """Require lowercase SHA-256 evidence rather than caller-readable benchmark data.""" + if type(value) is not str or _DIGEST_PATTERN.fullmatch(value) is None: + raise ValueError(f"{field_name} must be lowercase SHA-256 hex.") + return value + + +def _require_positive_integer(field_name: str, value: object) -> int: + """Require a strict positive version without accepting booleans.""" + if type(value) is not int or value <= 0: + raise ValueError(f"{field_name} must be a positive integer.") + return value + + +class CalibrationBenchmarkAuthorityRecord(tuple): + """Immutable owner projection for one released calibration benchmark. + + The record contains only opaque references, versions, digests, and temporal + release/correction evidence. Benchmark totals and protected source attributes + never cross this application boundary. + """ + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + benchmark_receipt_released_at: datetime, + owner_contract_released_at: datetime, + benchmark_receipt_superseded_at: datetime | None = None, + successor_benchmark_receipt_reference: str | None = None, + successor_benchmark_receipt_version: int | None = None, + successor_benchmark_receipt_digest: str | None = None, + successor_benchmark_receipt_released_at: datetime | None = None, + ) -> CalibrationBenchmarkAuthorityRecord: + """Validate and detach all authority-bearing benchmark evidence.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + benchmark_ref = _require_reference( + "benchmark_receipt_reference", + benchmark_receipt_reference, + "calibration_benchmark_receipt", + ) + benchmark_version = _require_positive_integer( + "benchmark_receipt_version", benchmark_receipt_version + ) + benchmark_digest = _require_digest( + "benchmark_receipt_digest", benchmark_receipt_digest + ) + owner_ref = _require_reference( + "benchmark_owner_contract_reference", + benchmark_owner_contract_reference, + "released_owner_contract", + ) + owner_version = _require_positive_integer( + "benchmark_owner_contract_version", benchmark_owner_contract_version + ) + owner_digest = _require_digest( + "benchmark_owner_contract_digest", benchmark_owner_contract_digest + ) + reference_at = _require_aware_datetime( + "benchmark_reference_at", benchmark_reference_at + ) + benchmark_released_at = _require_aware_datetime( + "benchmark_receipt_released_at", benchmark_receipt_released_at + ) + contract_released_at = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + if contract_released_at > benchmark_released_at: + raise ValueError( + "owner contract must be released no later than benchmark receipt." + ) + + supersession_values = ( + benchmark_receipt_superseded_at, + successor_benchmark_receipt_reference, + successor_benchmark_receipt_version, + successor_benchmark_receipt_digest, + successor_benchmark_receipt_released_at, + ) + if all(value is None for value in supersession_values): + superseded_at = None + successor_ref = None + successor_version = None + successor_digest = None + successor_released_at = None + elif any(value is None for value in supersession_values): + raise ValueError( + "benchmark supersession requires time and complete released successor coordinates." + ) + else: + superseded_at = _require_aware_datetime( + "benchmark_receipt_superseded_at", benchmark_receipt_superseded_at + ) + successor_ref = _require_reference( + "successor_benchmark_receipt_reference", + successor_benchmark_receipt_reference, + "calibration_benchmark_receipt", + ) + successor_version = _require_positive_integer( + "successor_benchmark_receipt_version", + successor_benchmark_receipt_version, + ) + successor_digest = _require_digest( + "successor_benchmark_receipt_digest", + successor_benchmark_receipt_digest, + ) + successor_released_at = _require_aware_datetime( + "successor_benchmark_receipt_released_at", + successor_benchmark_receipt_released_at, + ) + if superseded_at < benchmark_released_at: + raise ValueError( + "benchmark_receipt_superseded_at cannot precede release." + ) + if successor_version <= benchmark_version: + raise ValueError( + "successor benchmark receipt version must advance monotonically." + ) + if successor_digest == benchmark_digest: + raise ValueError( + "successor benchmark receipt digest must identify new evidence." + ) + if successor_released_at <= benchmark_released_at: + raise ValueError( + "successor benchmark receipt must be released after its predecessor." + ) + if successor_released_at != superseded_at: + raise ValueError( + "successor benchmark receipt must be released exactly at supersession." + ) + + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + benchmark_ref, + benchmark_version, + benchmark_digest, + owner_ref, + owner_version, + owner_digest, + reference_at, + benchmark_released_at, + contract_released_at, + superseded_at, + successor_ref, + successor_version, + successor_digest, + successor_released_at, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this owner evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity bound to this benchmark use.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def benchmark_receipt_reference(self) -> str: + """Return the immutable calibration-benchmark receipt reference.""" + return self[2] + + @property + def benchmark_receipt_version(self) -> int: + """Return the positive benchmark receipt version.""" + return self[3] + + @property + def benchmark_receipt_digest(self) -> str: + """Return the digest of the exact benchmark receipt bytes.""" + return self[4] + + @property + def benchmark_owner_contract_reference(self) -> str: + """Return the released benchmark-owner contract reference.""" + return self[5] + + @property + def benchmark_owner_contract_version(self) -> int: + """Return the released benchmark-owner contract version.""" + return self[6] + + @property + def benchmark_owner_contract_digest(self) -> str: + """Return the digest of the released benchmark-owner contract.""" + return self[7] + + @property + def benchmark_reference_at(self) -> datetime: + """Return the benchmark's authoritative reference instant.""" + return self[8] + + @property + def benchmark_receipt_released_at(self) -> datetime: + """Return when the benchmark receipt became released evidence.""" + return self[9] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the benchmark-owner contract became released evidence.""" + return self[10] + + @property + def benchmark_receipt_superseded_at(self) -> datetime | None: + """Return when this receipt stopped authorizing new scientific use.""" + return self[11] + + @property + def successor_benchmark_receipt_reference(self) -> str | None: + """Return the append-only successor receipt reference when corrected.""" + return self[12] + + @property + def successor_benchmark_receipt_version(self) -> int | None: + """Return the monotonically advanced successor receipt version.""" + return self[13] + + @property + def successor_benchmark_receipt_digest(self) -> str | None: + """Return the immutable successor evidence digest when corrected.""" + return self[14] + + @property + def successor_benchmark_receipt_released_at(self) -> datetime | None: + """Return when the owner released the append-only successor evidence.""" + return self[15] + + +class CalibrationBenchmarkAuthorityView: + """Sealed field-minimized benchmark evidence issued after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> CalibrationBenchmarkAuthorityView: + """Reject direct construction; only the resolver may issue this view.""" + raise TypeError( + "CalibrationBenchmarkAuthorityView is issued only by " + "resolve_calibration_benchmark_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("CalibrationBenchmarkAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("CalibrationBenchmarkAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_calibration_benchmark_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable released benchmark evidence without benchmark values.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class CalibrationBenchmarkAuthorityReadPort(Protocol): + """Owner read contract for released/versioned calibration benchmark evidence.""" + + def read_calibration_benchmark_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + ) -> CalibrationBenchmarkAuthorityRecord | None: + """Return matching released evidence or ``None`` through an owner ACL.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + CalibrationBenchmarkAuthorityReadPort, "read_calibration_benchmark_authority" +) + + +def _resolve_calibration_benchmark_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationBenchmarkAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize then corroborate the exact released benchmark tuple. + + The owner must independently resolve immutable release coordinates, release + instants, and append-only correction lineage. ``used_at`` is the scientific + receipt's use instant; it cannot precede release/reference authority or fall + on/after the receipt's owner-resolved supersession instant. No benchmark totals + or successor coordinates are returned to the caller. + """ + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_calibration_benchmark_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_calibration_benchmark_authority." + ) + + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + benchmark_ref = _require_reference( + "benchmark_receipt_reference", + benchmark_receipt_reference, + "calibration_benchmark_receipt", + ) + benchmark_version = _require_positive_integer( + "benchmark_receipt_version", benchmark_receipt_version + ) + benchmark_digest = _require_digest( + "benchmark_receipt_digest", benchmark_receipt_digest + ) + owner_ref = _require_reference( + "benchmark_owner_contract_reference", + benchmark_owner_contract_reference, + "released_owner_contract", + ) + owner_version = _require_positive_integer( + "benchmark_owner_contract_version", benchmark_owner_contract_version + ) + owner_digest = _require_digest( + "benchmark_owner_contract_digest", benchmark_owner_contract_digest + ) + reference_at = _require_aware_datetime( + "benchmark_reference_at", benchmark_reference_at + ) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=_restore_operational_uuid("tenant_record_id", tenant_identity), + validity_study_id=_restore_operational_uuid("validity_study_id", study_identity), + benchmark_receipt_reference=benchmark_ref, + benchmark_receipt_version=benchmark_version, + benchmark_receipt_digest=benchmark_digest, + benchmark_owner_contract_reference=owner_ref, + benchmark_owner_contract_version=owner_version, + benchmark_owner_contract_digest=owner_digest, + benchmark_reference_at=reference_at, + ) + if persisted is None: + raise CalibrationBenchmarkAuthorityNotFound(str(study_id)) + if type(persisted) is not CalibrationBenchmarkAuthorityRecord: + raise CalibrationBenchmarkAuthorityIntegrityError( + "owner port returned non-canonical calibration benchmark evidence" + ) + + try: + record = CalibrationBenchmarkAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + benchmark_receipt_reference=persisted.benchmark_receipt_reference, + benchmark_receipt_version=persisted.benchmark_receipt_version, + benchmark_receipt_digest=persisted.benchmark_receipt_digest, + benchmark_owner_contract_reference=persisted.benchmark_owner_contract_reference, + benchmark_owner_contract_version=persisted.benchmark_owner_contract_version, + benchmark_owner_contract_digest=persisted.benchmark_owner_contract_digest, + benchmark_reference_at=persisted.benchmark_reference_at, + benchmark_receipt_released_at=persisted.benchmark_receipt_released_at, + owner_contract_released_at=persisted.owner_contract_released_at, + benchmark_receipt_superseded_at=persisted.benchmark_receipt_superseded_at, + successor_benchmark_receipt_reference=( + persisted.successor_benchmark_receipt_reference + ), + successor_benchmark_receipt_version=( + persisted.successor_benchmark_receipt_version + ), + successor_benchmark_receipt_digest=persisted.successor_benchmark_receipt_digest, + successor_benchmark_receipt_released_at=( + persisted.successor_benchmark_receipt_released_at + ), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise CalibrationBenchmarkAuthorityIntegrityError( + "owner port returned malformed calibration benchmark evidence" + ) from exc + if record != persisted: + raise CalibrationBenchmarkAuthorityIntegrityError( + "owner port returned non-canonical calibration benchmark structure" + ) + + expected = ( + tenant_id, + study_id, + benchmark_ref, + benchmark_version, + benchmark_digest, + owner_ref, + owner_version, + owner_digest, + reference_at, + ) + observed = ( + record.tenant_record_id, + record.validity_study_id, + record.benchmark_receipt_reference, + record.benchmark_receipt_version, + record.benchmark_receipt_digest, + record.benchmark_owner_contract_reference, + record.benchmark_owner_contract_version, + record.benchmark_owner_contract_digest, + record.benchmark_reference_at, + ) + if observed != expected: + raise CalibrationBenchmarkAuthorityIntegrityError( + "released benchmark authority does not match the requested scientific coordinates" + ) + if ( + record.benchmark_reference_at > use_instant + or record.benchmark_receipt_released_at > use_instant + or record.owner_contract_released_at > use_instant + ): + raise CalibrationBenchmarkAuthorityIntegrityError( + "benchmark and owner evidence must exist no later than the scientific use instant" + ) + if ( + record.benchmark_receipt_superseded_at is not None + and record.benchmark_receipt_superseded_at <= use_instant + ): + raise CalibrationBenchmarkAuthorityIntegrityError( + "superseded benchmark evidence cannot authorize scientific use at or after correction" + ) + + fields: tuple[tuple[str, object], ...] = ( + ("benchmark_owner_contract_digest", record.benchmark_owner_contract_digest), + ("benchmark_owner_contract_reference", record.benchmark_owner_contract_reference), + ("benchmark_owner_contract_version", record.benchmark_owner_contract_version), + ("benchmark_receipt_digest", record.benchmark_receipt_digest), + ("benchmark_receipt_reference", record.benchmark_receipt_reference), + ("benchmark_receipt_released_at", record.benchmark_receipt_released_at), + ("benchmark_receipt_version", record.benchmark_receipt_version), + ("benchmark_reference_at", record.benchmark_reference_at), + ("owner_contract_released_at", record.owner_contract_released_at), + ) + return ( + _store_operational_uuid("tenant_record_id", tenant_id), + _store_operational_uuid("validity_study_id", study_id), + fields, + ) + + +def _build_calibration_benchmark_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: CalibrationBenchmarkAuthorityView) -> None: + """Verify one benchmark view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise CalibrationBenchmarkAuthorityIntegrityError( + "calibration benchmark view was not issued by " + "resolve_calibration_benchmark_authority" + ) from exc + if marker is not issuance_marker: + raise CalibrationBenchmarkAuthorityIntegrityError( + "calibration benchmark view was not issued by " + "resolve_calibration_benchmark_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationBenchmarkAuthorityReadPort, + ) -> CalibrationBenchmarkAuthorityView: + """Authorize and corroborate one exact released calibration benchmark.""" + tenant_identity, study_identity, fields = ( + _resolve_calibration_benchmark_authority_state( + **{ + name: value + for name, value in locals().items() + if name != "issuance_marker" + } + ) + ) + view = object.__new__(CalibrationBenchmarkAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_calibration_benchmark_view_issued, + resolve_calibration_benchmark_authority, +) = _build_calibration_benchmark_view_runtime() +del _build_calibration_benchmark_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_adjustment_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_adjustment_authority.py new file mode 100644 index 000000000..f90941d19 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_adjustment_authority.py @@ -0,0 +1,1184 @@ +"""Corroborate released typed calibration-adjustment evidence through an owner port. + +This application boundary binds the exact calibration receipt that produced a +point-weight artifact to its target population, analysis window, purpose-bound +auxiliary authority, benchmark authority, and primary or fallback generating +method. It does not copy auxiliary values, benchmark totals, protected +attributes, or row-level weights. Durable PostgreSQL/release resolution remains +a persistence-owner task after this service reaches protected truth. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "calibration_adjustment_authority" +_OPERATION = "read" +_TERMINATION_CODES = frozenset({"converged", "fallback_applied"}) +_READ_FIELDS = frozenset( + { + "calibration_receipt_reference", + "calibration_receipt_digest", + "evidence_version", + "target_population_digest", + "analysis_window_reference", + "auxiliary_authority_reference", + "auxiliary_projection_reference", + "auxiliary_projection_version", + "auxiliary_projection_digest", + "auxiliary_purpose_reference", + "auxiliary_purpose_digest", + "auxiliary_owner_contract_reference", + "auxiliary_owner_contract_version", + "auxiliary_owner_contract_digest", + "auxiliary_authorization_receipt_reference", + "auxiliary_authorization_receipt_digest", + "auxiliary_scientific_use_receipt_reference", + "auxiliary_scientific_use_receipt_digest", + "auxiliary_scientific_use_at", + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_receipt_digest", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_reference_at", + "algorithm_reference", + "algorithm_version", + "constraints_digest", + "termination_code", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "constructed_at", + "fallback_reason_code", + "fallback_rule_reference", + "fallback_rule_digest", + "fallback_algorithm_reference", + "fallback_algorithm_version", + "fallback_configuration_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class CalibrationAdjustmentAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the calibration receipt.""" + + +class CalibrationAdjustmentAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence cannot corroborate the requested calibration.""" + + +def _require_termination_code(value: object) -> str: + """Require an explicit successful-primary or explicit-fallback outcome.""" + if type(value) is not str or value not in _TERMINATION_CODES: + raise ValueError("termination_code must be converged or fallback_applied.") + return value + + +class CalibrationAdjustmentAuthorityRecord(tuple): + """Immutable owner projection for one released typed calibration receipt.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + target_population_digest: str, + analysis_window_reference: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + algorithm_reference: str, + algorithm_version: int, + constraints_digest: str, + termination_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + fallback_reason_code: str | None, + fallback_rule_reference: str | None, + fallback_rule_digest: str | None, + fallback_algorithm_reference: str | None, + fallback_algorithm_version: int | None, + fallback_configuration_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> CalibrationAdjustmentAuthorityRecord: + """Validate and detach the receipt-level scientific authority.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "calibration_receipt_reference", + calibration_receipt_reference, + "calibration_adjustment_receipt", + ) + receipt_digest = _require_digest( + "calibration_receipt_digest", calibration_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + target_digest = _require_digest( + "target_population_digest", target_population_digest + ) + analysis_window_ref = _require_reference( + "analysis_window_reference", analysis_window_reference, "analysis_window" + ) + auxiliary_authority_ref = _require_reference( + "auxiliary_authority_reference", + auxiliary_authority_reference, + "scientific_auxiliary_authority", + ) + auxiliary_projection_ref = _require_reference( + "auxiliary_projection_reference", + auxiliary_projection_reference, + "calibration_auxiliary_projection", + ) + auxiliary_projection_ver = _require_positive_integer( + "auxiliary_projection_version", auxiliary_projection_version + ) + projection_digest = _require_digest( + "auxiliary_projection_digest", auxiliary_projection_digest + ) + auxiliary_purpose_ref = _require_reference( + "auxiliary_purpose_reference", + auxiliary_purpose_reference, + "scientific_data_use_purpose", + ) + auxiliary_purpose_evidence = _require_digest( + "auxiliary_purpose_digest", auxiliary_purpose_digest + ) + auxiliary_owner_ref = _require_reference( + "auxiliary_owner_contract_reference", + auxiliary_owner_contract_reference, + "released_owner_contract", + ) + auxiliary_owner_ver = _require_positive_integer( + "auxiliary_owner_contract_version", auxiliary_owner_contract_version + ) + auxiliary_owner_evidence = _require_digest( + "auxiliary_owner_contract_digest", auxiliary_owner_contract_digest + ) + auxiliary_authorization_ref = _require_reference( + "auxiliary_authorization_receipt_reference", + auxiliary_authorization_receipt_reference, + "scientific_data_authorization", + ) + auxiliary_authorization_evidence = _require_digest( + "auxiliary_authorization_receipt_digest", + auxiliary_authorization_receipt_digest, + ) + auxiliary_use_ref = _require_reference( + "auxiliary_scientific_use_receipt_reference", + auxiliary_scientific_use_receipt_reference, + "scientific_use_receipt", + ) + auxiliary_use_evidence = _require_digest( + "auxiliary_scientific_use_receipt_digest", + auxiliary_scientific_use_receipt_digest, + ) + auxiliary_use_at = _require_aware_datetime( + "auxiliary_scientific_use_at", auxiliary_scientific_use_at + ) + benchmark_ref = _require_reference( + "benchmark_receipt_reference", + benchmark_receipt_reference, + "calibration_benchmark_receipt", + ) + benchmark_version = _require_positive_integer( + "benchmark_receipt_version", benchmark_receipt_version + ) + benchmark_digest = _require_digest( + "benchmark_receipt_digest", benchmark_receipt_digest + ) + benchmark_owner_ref = _require_reference( + "benchmark_owner_contract_reference", + benchmark_owner_contract_reference, + "released_owner_contract", + ) + benchmark_owner_ver = _require_positive_integer( + "benchmark_owner_contract_version", benchmark_owner_contract_version + ) + benchmark_owner_evidence = _require_digest( + "benchmark_owner_contract_digest", benchmark_owner_contract_digest + ) + benchmark_at = _require_aware_datetime( + "benchmark_reference_at", benchmark_reference_at + ) + algorithm_ref = _require_reference( + "algorithm_reference", algorithm_reference, "calibration_algorithm" + ) + algorithm_ver = _require_positive_integer("algorithm_version", algorithm_version) + constraints = _require_digest("constraints_digest", constraints_digest) + termination = _require_termination_code(termination_code) + input_digest = _require_digest( + "input_weight_artifact_digest", input_weight_artifact_digest + ) + output_digest = _require_digest( + "output_weight_artifact_digest", output_weight_artifact_digest + ) + if input_digest == output_digest: + raise ValueError( + "output_weight_artifact_digest must identify the calibrated weight artifact." + ) + constructed = _require_aware_datetime("constructed_at", constructed_at) + if auxiliary_use_at > constructed: + raise ValueError( + "auxiliary_scientific_use_at cannot be later than constructed_at." + ) + if benchmark_at > constructed: + raise ValueError("benchmark_reference_at cannot be later than constructed_at.") + + fallback_values = ( + fallback_reason_code, + fallback_rule_reference, + fallback_rule_digest, + fallback_algorithm_reference, + fallback_algorithm_version, + fallback_configuration_digest, + ) + if termination == "fallback_applied": + if any(value is None for value in fallback_values): + raise ValueError( + "fallback reason, rule, algorithm, version, and configuration evidence " + "are required for fallback_applied." + ) + fallback_reason = _require_code("fallback_reason_code", fallback_reason_code) + fallback_rule_ref = _require_reference( + "fallback_rule_reference", + fallback_rule_reference, + "calibration_fallback_rule", + ) + fallback_rule_evidence = _require_digest( + "fallback_rule_digest", fallback_rule_digest + ) + fallback_algorithm_ref = _require_reference( + "fallback_algorithm_reference", + fallback_algorithm_reference, + "calibration_algorithm", + ) + fallback_algorithm_ver = _require_positive_integer( + "fallback_algorithm_version", fallback_algorithm_version + ) + fallback_configuration = _require_digest( + "fallback_configuration_digest", fallback_configuration_digest + ) + else: + if any(value is not None for value in fallback_values): + raise ValueError("fallback evidence must be absent when calibration converged.") + fallback_reason = None + fallback_rule_ref = None + fallback_rule_evidence = None + fallback_algorithm_ref = None + fallback_algorithm_ver = None + fallback_configuration = None + + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_released = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + supersession_instant = ( + None + if superseded_at is None + else _require_aware_datetime("superseded_at", superseded_at) + ) + if release_instant < constructed: + raise ValueError("released_at cannot precede constructed_at.") + if owner_released > release_instant: + raise ValueError( + "owner_contract_released_at cannot be later than released_at." + ) + if supersession_instant is not None and supersession_instant <= release_instant: + raise ValueError("superseded_at must be later than released_at.") + + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + receipt_ref, + receipt_digest, + version, + projection_digest, + benchmark_digest, + algorithm_ref, + algorithm_ver, + constraints, + termination, + input_digest, + output_digest, + constructed, + fallback_reason, + fallback_rule_ref, + fallback_rule_evidence, + fallback_algorithm_ref, + fallback_algorithm_ver, + fallback_configuration, + owner_ref, + owner_version, + owner_digest, + owner_released, + release_instant, + target_digest, + analysis_window_ref, + auxiliary_authority_ref, + auxiliary_projection_ref, + auxiliary_projection_ver, + auxiliary_purpose_ref, + auxiliary_purpose_evidence, + auxiliary_owner_ref, + auxiliary_owner_ver, + auxiliary_owner_evidence, + auxiliary_authorization_ref, + auxiliary_authorization_evidence, + auxiliary_use_ref, + auxiliary_use_evidence, + auxiliary_use_at, + benchmark_ref, + benchmark_version, + benchmark_owner_ref, + benchmark_owner_ver, + benchmark_owner_evidence, + benchmark_at, + supersession_instant, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this released evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity for this released evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def calibration_receipt_reference(self) -> str: + """Return the typed calibration-adjustment receipt reference.""" + return self[2] + + @property + def calibration_receipt_digest(self) -> str: + """Return the exact calibration-adjustment receipt digest.""" + return self[3] + + @property + def evidence_version(self) -> int: + """Return the receipt evidence version.""" + return self[4] + + @property + def auxiliary_projection_digest(self) -> str: + """Return the purpose-limited auxiliary projection digest used by the receipt.""" + return self[5] + + @property + def benchmark_receipt_digest(self) -> str: + """Return the benchmark receipt digest used by the receipt.""" + return self[6] + + @property + def algorithm_reference(self) -> str: + """Return the primary calibration algorithm reference.""" + return self[7] + + @property + def algorithm_version(self) -> int: + """Return the primary calibration algorithm version.""" + return self[8] + + @property + def constraints_digest(self) -> str: + """Return the immutable calibration constraints digest.""" + return self[9] + + @property + def termination_code(self) -> str: + """Return whether the primary method converged or fallback produced weights.""" + return self[10] + + @property + def input_weight_artifact_digest(self) -> str: + """Return the input weight artifact digest.""" + return self[11] + + @property + def output_weight_artifact_digest(self) -> str: + """Return the calibrated output weight artifact digest.""" + return self[12] + + @property + def constructed_at(self) -> datetime: + """Return when the typed calibration receipt was constructed.""" + return self[13] + + @property + def fallback_reason_code(self) -> str | None: + """Return the primary failure reason when fallback produced the weights.""" + return self[14] + + @property + def fallback_rule_reference(self) -> str | None: + """Return the immutable fallback-rule reference when fallback was applied.""" + return self[15] + + @property + def fallback_rule_digest(self) -> str | None: + """Return the immutable fallback-rule digest when fallback was applied.""" + return self[16] + + @property + def fallback_algorithm_reference(self) -> str | None: + """Return the actual algorithm that produced fallback weights.""" + return self[17] + + @property + def fallback_algorithm_version(self) -> int | None: + """Return the actual fallback algorithm version.""" + return self[18] + + @property + def fallback_configuration_digest(self) -> str | None: + """Return the actual fallback configuration digest when fallback was applied.""" + return self[19] + + @property + def owner_contract_reference(self) -> str: + """Return the released application owner-contract reference.""" + return self[20] + + @property + def owner_contract_version(self) -> int: + """Return the released application owner-contract version.""" + return self[21] + + @property + def owner_contract_digest(self) -> str: + """Return the released application owner-contract digest.""" + return self[22] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the governing application owner contract became authority.""" + return self[23] + + @property + def released_at(self) -> datetime: + """Return when this typed calibration evidence became released authority.""" + return self[24] + + @property + def target_population_digest(self) -> str: + """Return the exact target population governed by the calibration receipt.""" + return self[25] + + @property + def analysis_window_reference(self) -> str: + """Return the analysis window governed by the calibration receipt.""" + return self[26] + + @property + def auxiliary_authority_reference(self) -> str: + """Return the scientific auxiliary-authority reference.""" + return self[27] + + @property + def auxiliary_projection_reference(self) -> str: + """Return the exact purpose-limited auxiliary projection reference.""" + return self[28] + + @property + def auxiliary_projection_version(self) -> int: + """Return the exact purpose-limited auxiliary projection version.""" + return self[29] + + @property + def auxiliary_purpose_reference(self) -> str: + """Return the scientific data-use purpose reference.""" + return self[30] + + @property + def auxiliary_purpose_digest(self) -> str: + """Return the scientific data-use purpose digest.""" + return self[31] + + @property + def auxiliary_owner_contract_reference(self) -> str: + """Return the auxiliary owner's released contract reference.""" + return self[32] + + @property + def auxiliary_owner_contract_version(self) -> int: + """Return the auxiliary owner's released contract version.""" + return self[33] + + @property + def auxiliary_owner_contract_digest(self) -> str: + """Return the auxiliary owner's released contract digest.""" + return self[34] + + @property + def auxiliary_authorization_receipt_reference(self) -> str: + """Return the purpose-bound auxiliary authorization receipt reference.""" + return self[35] + + @property + def auxiliary_authorization_receipt_digest(self) -> str: + """Return the purpose-bound auxiliary authorization receipt digest.""" + return self[36] + + @property + def auxiliary_scientific_use_receipt_reference(self) -> str: + """Return the scientific-use receipt reference.""" + return self[37] + + @property + def auxiliary_scientific_use_receipt_digest(self) -> str: + """Return the scientific-use receipt digest.""" + return self[38] + + @property + def auxiliary_scientific_use_at(self) -> datetime: + """Return the exact scientific-use instant committed by the receipt.""" + return self[39] + + @property + def benchmark_receipt_reference(self) -> str: + """Return the exact calibration benchmark receipt reference.""" + return self[40] + + @property + def benchmark_receipt_version(self) -> int: + """Return the exact calibration benchmark receipt version.""" + return self[41] + + @property + def benchmark_owner_contract_reference(self) -> str: + """Return the benchmark owner's released contract reference.""" + return self[42] + + @property + def benchmark_owner_contract_version(self) -> int: + """Return the benchmark owner's released contract version.""" + return self[43] + + @property + def benchmark_owner_contract_digest(self) -> str: + """Return the benchmark owner's released contract digest.""" + return self[44] + + @property + def benchmark_reference_at(self) -> datetime: + """Return the exact benchmark reference instant committed by the receipt.""" + return self[45] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive owner-resolved cutover for this calibration receipt.""" + return self[46] + + +class CalibrationAdjustmentAuthorityView: + """Field-minimized typed calibration evidence issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> CalibrationAdjustmentAuthorityView: + """Reject direct construction; only the resolver may issue this view.""" + raise TypeError( + "CalibrationAdjustmentAuthorityView is issued only by " + "resolve_calibration_adjustment_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Reject mutation after resolver-controlled issuance.""" + raise AttributeError("CalibrationAdjustmentAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Reject deletion after resolver-controlled issuance.""" + raise AttributeError("CalibrationAdjustmentAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Require the exact in-process marker written by the resolver.""" + _require_calibration_adjustment_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable typed calibration provenance without source values.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class CalibrationAdjustmentAuthorityReadPort(Protocol): + """Owner read contract for released typed calibration-adjustment evidence.""" + + def read_calibration_adjustment_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + target_population_digest: str, + analysis_window_reference: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + algorithm_reference: str, + algorithm_version: int, + constraints_digest: str, + termination_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + fallback_reason_code: str | None, + fallback_rule_reference: str | None, + fallback_rule_digest: str | None, + fallback_algorithm_reference: str | None, + fallback_algorithm_version: int | None, + fallback_configuration_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> CalibrationAdjustmentAuthorityRecord | None: + """Return matching released typed calibration evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + CalibrationAdjustmentAuthorityReadPort, "read_calibration_adjustment_authority" +) + + +def _coordinate_tuple(record: CalibrationAdjustmentAuthorityRecord) -> tuple[object, ...]: + """Return caller-known coordinates, excluding owner-resolved chronology.""" + return record[:23] + record[25:46] + + +def _resolve_calibration_adjustment_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + target_population_digest: str, + analysis_window_reference: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + algorithm_reference: str, + algorithm_version: int, + constraints_digest: str, + termination_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + fallback_reason_code: str | None, + fallback_rule_reference: str | None, + fallback_rule_digest: str | None, + fallback_algorithm_reference: str | None, + fallback_algorithm_version: int | None, + fallback_configuration_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationAdjustmentAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and corroborate calibration evidence into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_calibration_adjustment_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_calibration_adjustment_authority." + ) + + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + requested = CalibrationAdjustmentAuthorityRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + calibration_receipt_reference=calibration_receipt_reference, + calibration_receipt_digest=calibration_receipt_digest, + evidence_version=evidence_version, + target_population_digest=target_population_digest, + analysis_window_reference=analysis_window_reference, + auxiliary_authority_reference=auxiliary_authority_reference, + auxiliary_projection_reference=auxiliary_projection_reference, + auxiliary_projection_version=auxiliary_projection_version, + auxiliary_projection_digest=auxiliary_projection_digest, + auxiliary_purpose_reference=auxiliary_purpose_reference, + auxiliary_purpose_digest=auxiliary_purpose_digest, + auxiliary_owner_contract_reference=auxiliary_owner_contract_reference, + auxiliary_owner_contract_version=auxiliary_owner_contract_version, + auxiliary_owner_contract_digest=auxiliary_owner_contract_digest, + auxiliary_authorization_receipt_reference=auxiliary_authorization_receipt_reference, + auxiliary_authorization_receipt_digest=auxiliary_authorization_receipt_digest, + auxiliary_scientific_use_receipt_reference=auxiliary_scientific_use_receipt_reference, + auxiliary_scientific_use_receipt_digest=auxiliary_scientific_use_receipt_digest, + auxiliary_scientific_use_at=auxiliary_scientific_use_at, + benchmark_receipt_reference=benchmark_receipt_reference, + benchmark_receipt_version=benchmark_receipt_version, + benchmark_receipt_digest=benchmark_receipt_digest, + benchmark_owner_contract_reference=benchmark_owner_contract_reference, + benchmark_owner_contract_version=benchmark_owner_contract_version, + benchmark_owner_contract_digest=benchmark_owner_contract_digest, + benchmark_reference_at=benchmark_reference_at, + algorithm_reference=algorithm_reference, + algorithm_version=algorithm_version, + constraints_digest=constraints_digest, + termination_code=termination_code, + input_weight_artifact_digest=input_weight_artifact_digest, + output_weight_artifact_digest=output_weight_artifact_digest, + constructed_at=constructed_at, + fallback_reason_code=fallback_reason_code, + fallback_rule_reference=fallback_rule_reference, + fallback_rule_digest=fallback_rule_digest, + fallback_algorithm_reference=fallback_algorithm_reference, + fallback_algorithm_version=fallback_algorithm_version, + fallback_configuration_digest=fallback_configuration_digest, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + owner_contract_released_at=constructed_at, + released_at=constructed_at, + superseded_at=None, + ) + tenant_id = requested.tenant_record_id + study_id = requested.validity_study_id + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=requested.tenant_record_id, + validity_study_id=requested.validity_study_id, + calibration_receipt_reference=requested.calibration_receipt_reference, + calibration_receipt_digest=requested.calibration_receipt_digest, + evidence_version=requested.evidence_version, + target_population_digest=requested.target_population_digest, + analysis_window_reference=requested.analysis_window_reference, + auxiliary_authority_reference=requested.auxiliary_authority_reference, + auxiliary_projection_reference=requested.auxiliary_projection_reference, + auxiliary_projection_version=requested.auxiliary_projection_version, + auxiliary_projection_digest=requested.auxiliary_projection_digest, + auxiliary_purpose_reference=requested.auxiliary_purpose_reference, + auxiliary_purpose_digest=requested.auxiliary_purpose_digest, + auxiliary_owner_contract_reference=requested.auxiliary_owner_contract_reference, + auxiliary_owner_contract_version=requested.auxiliary_owner_contract_version, + auxiliary_owner_contract_digest=requested.auxiliary_owner_contract_digest, + auxiliary_authorization_receipt_reference=requested.auxiliary_authorization_receipt_reference, + auxiliary_authorization_receipt_digest=requested.auxiliary_authorization_receipt_digest, + auxiliary_scientific_use_receipt_reference=requested.auxiliary_scientific_use_receipt_reference, + auxiliary_scientific_use_receipt_digest=requested.auxiliary_scientific_use_receipt_digest, + auxiliary_scientific_use_at=requested.auxiliary_scientific_use_at, + benchmark_receipt_reference=requested.benchmark_receipt_reference, + benchmark_receipt_version=requested.benchmark_receipt_version, + benchmark_receipt_digest=requested.benchmark_receipt_digest, + benchmark_owner_contract_reference=requested.benchmark_owner_contract_reference, + benchmark_owner_contract_version=requested.benchmark_owner_contract_version, + benchmark_owner_contract_digest=requested.benchmark_owner_contract_digest, + benchmark_reference_at=requested.benchmark_reference_at, + algorithm_reference=requested.algorithm_reference, + algorithm_version=requested.algorithm_version, + constraints_digest=requested.constraints_digest, + termination_code=requested.termination_code, + input_weight_artifact_digest=requested.input_weight_artifact_digest, + output_weight_artifact_digest=requested.output_weight_artifact_digest, + constructed_at=requested.constructed_at, + fallback_reason_code=requested.fallback_reason_code, + fallback_rule_reference=requested.fallback_rule_reference, + fallback_rule_digest=requested.fallback_rule_digest, + fallback_algorithm_reference=requested.fallback_algorithm_reference, + fallback_algorithm_version=requested.fallback_algorithm_version, + fallback_configuration_digest=requested.fallback_configuration_digest, + owner_contract_reference=requested.owner_contract_reference, + owner_contract_version=requested.owner_contract_version, + owner_contract_digest=requested.owner_contract_digest, + ) + if persisted is None: + raise CalibrationAdjustmentAuthorityNotFound(str(study_id)) + if type(persisted) is not CalibrationAdjustmentAuthorityRecord: + raise CalibrationAdjustmentAuthorityIntegrityError( + "owner port returned non-canonical calibration-adjustment authority evidence" + ) + + try: + record = CalibrationAdjustmentAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + calibration_receipt_reference=persisted.calibration_receipt_reference, + calibration_receipt_digest=persisted.calibration_receipt_digest, + evidence_version=persisted.evidence_version, + target_population_digest=persisted.target_population_digest, + analysis_window_reference=persisted.analysis_window_reference, + auxiliary_authority_reference=persisted.auxiliary_authority_reference, + auxiliary_projection_reference=persisted.auxiliary_projection_reference, + auxiliary_projection_version=persisted.auxiliary_projection_version, + auxiliary_projection_digest=persisted.auxiliary_projection_digest, + auxiliary_purpose_reference=persisted.auxiliary_purpose_reference, + auxiliary_purpose_digest=persisted.auxiliary_purpose_digest, + auxiliary_owner_contract_reference=persisted.auxiliary_owner_contract_reference, + auxiliary_owner_contract_version=persisted.auxiliary_owner_contract_version, + auxiliary_owner_contract_digest=persisted.auxiliary_owner_contract_digest, + auxiliary_authorization_receipt_reference=persisted.auxiliary_authorization_receipt_reference, + auxiliary_authorization_receipt_digest=persisted.auxiliary_authorization_receipt_digest, + auxiliary_scientific_use_receipt_reference=persisted.auxiliary_scientific_use_receipt_reference, + auxiliary_scientific_use_receipt_digest=persisted.auxiliary_scientific_use_receipt_digest, + auxiliary_scientific_use_at=persisted.auxiliary_scientific_use_at, + benchmark_receipt_reference=persisted.benchmark_receipt_reference, + benchmark_receipt_version=persisted.benchmark_receipt_version, + benchmark_receipt_digest=persisted.benchmark_receipt_digest, + benchmark_owner_contract_reference=persisted.benchmark_owner_contract_reference, + benchmark_owner_contract_version=persisted.benchmark_owner_contract_version, + benchmark_owner_contract_digest=persisted.benchmark_owner_contract_digest, + benchmark_reference_at=persisted.benchmark_reference_at, + algorithm_reference=persisted.algorithm_reference, + algorithm_version=persisted.algorithm_version, + constraints_digest=persisted.constraints_digest, + termination_code=persisted.termination_code, + input_weight_artifact_digest=persisted.input_weight_artifact_digest, + output_weight_artifact_digest=persisted.output_weight_artifact_digest, + constructed_at=persisted.constructed_at, + fallback_reason_code=persisted.fallback_reason_code, + fallback_rule_reference=persisted.fallback_rule_reference, + fallback_rule_digest=persisted.fallback_rule_digest, + fallback_algorithm_reference=persisted.fallback_algorithm_reference, + fallback_algorithm_version=persisted.fallback_algorithm_version, + fallback_configuration_digest=persisted.fallback_configuration_digest, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise CalibrationAdjustmentAuthorityIntegrityError( + "owner port returned malformed calibration-adjustment authority evidence" + ) from exc + if record != persisted: + raise CalibrationAdjustmentAuthorityIntegrityError( + "owner port returned non-canonical calibration-adjustment authority evidence" + ) + if _coordinate_tuple(record) != _coordinate_tuple(requested): + raise CalibrationAdjustmentAuthorityIntegrityError( + "released calibration-adjustment authority does not match requested coordinates" + ) + if record.released_at > use_instant: + raise CalibrationAdjustmentAuthorityIntegrityError( + "calibration-adjustment evidence must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise CalibrationAdjustmentAuthorityIntegrityError( + "calibration-adjustment evidence is superseded for this scientific-use instant" + ) + + fields: tuple[tuple[str, object], ...] = ( + ("algorithm_reference", record.algorithm_reference), + ("algorithm_version", record.algorithm_version), + ("analysis_window_reference", record.analysis_window_reference), + ("auxiliary_authority_reference", record.auxiliary_authority_reference), + ("auxiliary_authorization_receipt_digest", record.auxiliary_authorization_receipt_digest), + ("auxiliary_authorization_receipt_reference", record.auxiliary_authorization_receipt_reference), + ("auxiliary_owner_contract_digest", record.auxiliary_owner_contract_digest), + ("auxiliary_owner_contract_reference", record.auxiliary_owner_contract_reference), + ("auxiliary_owner_contract_version", record.auxiliary_owner_contract_version), + ("auxiliary_projection_digest", record.auxiliary_projection_digest), + ("auxiliary_projection_reference", record.auxiliary_projection_reference), + ("auxiliary_projection_version", record.auxiliary_projection_version), + ("auxiliary_purpose_digest", record.auxiliary_purpose_digest), + ("auxiliary_purpose_reference", record.auxiliary_purpose_reference), + ("auxiliary_scientific_use_at", record.auxiliary_scientific_use_at), + ("auxiliary_scientific_use_receipt_digest", record.auxiliary_scientific_use_receipt_digest), + ("auxiliary_scientific_use_receipt_reference", record.auxiliary_scientific_use_receipt_reference), + ("benchmark_owner_contract_digest", record.benchmark_owner_contract_digest), + ("benchmark_owner_contract_reference", record.benchmark_owner_contract_reference), + ("benchmark_owner_contract_version", record.benchmark_owner_contract_version), + ("benchmark_receipt_digest", record.benchmark_receipt_digest), + ("benchmark_receipt_reference", record.benchmark_receipt_reference), + ("benchmark_receipt_version", record.benchmark_receipt_version), + ("benchmark_reference_at", record.benchmark_reference_at), + ("calibration_receipt_digest", record.calibration_receipt_digest), + ("calibration_receipt_reference", record.calibration_receipt_reference), + ("constraints_digest", record.constraints_digest), + ("constructed_at", record.constructed_at), + ("evidence_version", record.evidence_version), + ("input_weight_artifact_digest", record.input_weight_artifact_digest), + ("output_weight_artifact_digest", record.output_weight_artifact_digest), + ("owner_contract_digest", record.owner_contract_digest), + ("owner_contract_reference", record.owner_contract_reference), + ("owner_contract_released_at", record.owner_contract_released_at), + ("owner_contract_version", record.owner_contract_version), + ("released_at", record.released_at), + ("target_population_digest", record.target_population_digest), + ("termination_code", record.termination_code), + ) + if record.termination_code == "fallback_applied": + fields += ( + ("fallback_algorithm_reference", record.fallback_algorithm_reference), + ("fallback_algorithm_version", record.fallback_algorithm_version), + ("fallback_configuration_digest", record.fallback_configuration_digest), + ("fallback_reason_code", record.fallback_reason_code), + ("fallback_rule_digest", record.fallback_rule_digest), + ("fallback_rule_reference", record.fallback_rule_reference), + ) + return ( + _store_operational_uuid("tenant_record_id", tenant_id), + _store_operational_uuid("validity_study_id", study_id), + fields, + ) + + +def _build_calibration_adjustment_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: CalibrationAdjustmentAuthorityView) -> None: + """Verify one calibration view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise CalibrationAdjustmentAuthorityIntegrityError( + "calibration-adjustment authority view was not issued by the resolver" + ) from exc + if marker is not issuance_marker: + raise CalibrationAdjustmentAuthorityIntegrityError( + "calibration-adjustment authority view has an invalid issuance marker" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + target_population_digest: str, + analysis_window_reference: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + algorithm_reference: str, + algorithm_version: int, + constraints_digest: str, + termination_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + fallback_reason_code: str | None, + fallback_rule_reference: str | None, + fallback_rule_digest: str | None, + fallback_algorithm_reference: str | None, + fallback_algorithm_version: int | None, + fallback_configuration_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationAdjustmentAuthorityReadPort, + ) -> CalibrationAdjustmentAuthorityView: + """Authorize then corroborate the exact released calibration receipt.""" + tenant_identity, study_identity, fields = ( + _resolve_calibration_adjustment_authority_state( + **{ + name: value + for name, value in locals().items() + if name != "issuance_marker" + } + ) + ) + view = object.__new__(CalibrationAdjustmentAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_calibration_adjustment_view_issued, + resolve_calibration_adjustment_authority, +) = _build_calibration_adjustment_view_runtime() +del _build_calibration_adjustment_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_adjustment_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_adjustment_supersession_authority.py new file mode 100644 index 000000000..fe29d7caf --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_adjustment_supersession_authority.py @@ -0,0 +1,565 @@ +"""Corroborate append-only typed calibration-adjustment correction authority. + +The ordinary calibration projection proves which released calibration receipt +produced a point-weight artifact. This boundary proves the half-open authority +interval for that immutable receipt and, when corrected, the exact released +successor that ends the interval. Successor chronology is owner-resolved and is +never accepted as a caller-selected lookup coordinate. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "calibration_adjustment_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "calibration_receipt_reference", + "calibration_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_calibration_receipt_reference", + "successor_calibration_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class CalibrationAdjustmentSupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the calibration receipt.""" + + +class CalibrationAdjustmentSupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released calibration correction evidence cannot authorize use.""" + + +class CalibrationAdjustmentSupersessionAuthorityRecord(tuple): + """Immutable owner projection for one calibration receipt authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_calibration_receipt_reference: str | None = None, + successor_calibration_receipt_digest: str | None = None, + successor_evidence_version: int | None = None, + successor_released_at: datetime | None = None, + ) -> CalibrationAdjustmentSupersessionAuthorityRecord: + """Validate one released predecessor and its optional atomic successor edge.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "calibration_receipt_reference", + calibration_receipt_reference, + "calibration_adjustment_receipt", + ) + receipt_digest = _require_digest( + "calibration_receipt_digest", calibration_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release > release_instant: + raise ValueError( + "owner contract must be released no later than calibration receipt." + ) + + successor_values = ( + superseded_at, + successor_calibration_receipt_reference, + successor_calibration_receipt_digest, + successor_evidence_version, + successor_released_at, + ) + if all(value is None for value in successor_values): + cutover = None + successor_ref = None + successor_digest = None + successor_version = None + successor_release = None + elif any(value is None for value in successor_values): + raise ValueError( + "calibration supersession requires cutover and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_calibration_receipt_reference", + successor_calibration_receipt_reference, + "calibration_adjustment_receipt", + ) + successor_digest = _require_digest( + "successor_calibration_receipt_digest", + successor_calibration_receipt_digest, + ) + successor_version = _require_positive_integer( + "successor_evidence_version", successor_evidence_version + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover <= release_instant: + raise ValueError("superseded_at must be later than calibration receipt release.") + if successor_ref == receipt_ref: + raise ValueError("successor calibration receipt must have a new reference.") + if successor_digest == receipt_digest: + raise ValueError("successor calibration receipt must identify new evidence.") + if successor_version != 1: + raise ValueError("successor_evidence_version must remain 1.") + if successor_release <= release_instant: + raise ValueError( + "successor calibration receipt must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor calibration receipt must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("calibration_receipt_digest", receipt_digest), + ("calibration_receipt_reference", receipt_ref), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", owner_release), + ("owner_contract_version", owner_version), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_calibration_receipt_digest", successor_digest), + ("successor_calibration_receipt_reference", successor_ref), + ("successor_evidence_version", successor_version), + ("successor_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-receipt authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this calibration receipt became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this receipt's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class CalibrationAdjustmentSupersessionAuthorityView: + """Sealed current-receipt authority issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> CalibrationAdjustmentSupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "CalibrationAdjustmentSupersessionAuthorityView is issued only by " + "resolve_calibration_adjustment_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("CalibrationAdjustmentSupersessionAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("CalibrationAdjustmentSupersessionAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_calibration_adjustment_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return current receipt authority without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class CalibrationAdjustmentSupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released calibration correction state.""" + + def read_calibration_adjustment_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> CalibrationAdjustmentSupersessionAuthorityRecord | None: + """Return matching released calibration supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + CalibrationAdjustmentSupersessionAuthorityReadPort, + "read_calibration_adjustment_supersession_authority", +) + + +def _resolve_calibration_adjustment_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationAdjustmentSupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve calibration supersession into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_calibration_adjustment_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_calibration_adjustment_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + receipt_ref = _require_reference( + "calibration_receipt_reference", + calibration_receipt_reference, + "calibration_adjustment_receipt", + ) + receipt_digest = _require_digest( + "calibration_receipt_digest", calibration_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + calibration_receipt_reference=receipt_ref, + calibration_receipt_digest=receipt_digest, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise CalibrationAdjustmentSupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not CalibrationAdjustmentSupersessionAuthorityRecord: + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "owner port returned non-canonical calibration supersession evidence" + ) + + try: + persisted_fields = dict(persisted.fields) + persisted_successor = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = CalibrationAdjustmentSupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + calibration_receipt_reference=persisted_fields[ + "calibration_receipt_reference" + ], + calibration_receipt_digest=persisted_fields[ + "calibration_receipt_digest" + ], + evidence_version=persisted_fields["evidence_version"], + owner_contract_reference=persisted_fields["owner_contract_reference"], + owner_contract_version=persisted_fields["owner_contract_version"], + owner_contract_digest=persisted_fields["owner_contract_digest"], + owner_contract_released_at=persisted_fields["owner_contract_released_at"], + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_calibration_receipt_reference=( + None + if persisted_successor is None + else persisted_successor["successor_calibration_receipt_reference"] + ), + successor_calibration_receipt_digest=( + None + if persisted_successor is None + else persisted_successor["successor_calibration_receipt_digest"] + ), + successor_evidence_version=( + None + if persisted_successor is None + else persisted_successor["successor_evidence_version"] + ), + successor_released_at=( + None + if persisted_successor is None + else persisted_successor["successor_released_at"] + ), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "owner port returned malformed calibration supersession evidence" + ) from exc + if record != persisted: + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "owner port returned non-canonical calibration supersession structure" + ) + + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["calibration_receipt_reference"] != receipt_ref + or record_values["calibration_receipt_digest"] != receipt_digest + or record_values["evidence_version"] != version + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "released calibration supersession authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "calibration receipt must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "calibration receipt is superseded for this scientific-use instant" + ) + + fields = record.fields + ( + ("released_at", record.released_at), + ("superseded_at", record.superseded_at), + ) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_calibration_adjustment_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: CalibrationAdjustmentSupersessionAuthorityView) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "calibration-adjustment supersession view was not issued by " + "resolve_calibration_adjustment_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise CalibrationAdjustmentSupersessionAuthorityIntegrityError( + "calibration-adjustment supersession view was not issued by " + "resolve_calibration_adjustment_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationAdjustmentSupersessionAuthorityReadPort, + ) -> CalibrationAdjustmentSupersessionAuthorityView: + """Authorize then resolve the calibration receipt authority interval.""" + tenant_identity, study_identity, fields = ( + _resolve_calibration_adjustment_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + calibration_receipt_reference=calibration_receipt_reference, + calibration_receipt_digest=calibration_receipt_digest, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(CalibrationAdjustmentSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_calibration_adjustment_supersession_view_issued, + resolve_calibration_adjustment_supersession_authority, +) = _build_calibration_adjustment_supersession_view_runtime() +del _build_calibration_adjustment_supersession_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_support_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_support_authority.py new file mode 100644 index 000000000..315ecd8bd --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/calibration_support_authority.py @@ -0,0 +1,917 @@ +"""Corroborate released auxiliary/benchmark chronology for one calibration receipt. + +The typed calibration receipt carries the identities of the auxiliary and benchmark +inputs used to construct weights. This boundary separately proves that those exact +supporting authorities were released and current when the calibration was +constructed. Owner-resolved release, effective-interval, and cutover instants are +evidence rather than caller lookup coordinates. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "calibration_support_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "support_authority_reference", + "support_authority_digest", + "evidence_version", + "calibration_receipt_reference", + "calibration_receipt_digest", + "auxiliary_authority_reference", + "auxiliary_projection_reference", + "auxiliary_projection_version", + "auxiliary_projection_digest", + "auxiliary_purpose_reference", + "auxiliary_purpose_digest", + "auxiliary_owner_contract_reference", + "auxiliary_owner_contract_version", + "auxiliary_owner_contract_digest", + "auxiliary_owner_contract_released_at", + "auxiliary_authorization_receipt_reference", + "auxiliary_authorization_receipt_digest", + "auxiliary_authorization_receipt_released_at", + "auxiliary_scientific_use_receipt_reference", + "auxiliary_scientific_use_receipt_digest", + "auxiliary_scientific_use_at", + "auxiliary_authorized_from", + "auxiliary_authorized_to", + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_receipt_digest", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_owner_contract_released_at", + "benchmark_reference_at", + "benchmark_receipt_released_at", + "benchmark_receipt_superseded_at", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class CalibrationSupportAuthorityNotFound(LookupError): + """Indicate that no released support binding matches the requested calibration.""" + + +class CalibrationSupportAuthorityIntegrityError(RuntimeError): + """Indicate that returned support evidence does not match the requested binding.""" + + +def _tuple_property(index: int, doc: str) -> property: + """Create a documented immutable tuple projection for one public evidence field.""" + + def getter(record: tuple[object, ...]) -> object: + """Return one already-validated immutable evidence coordinate.""" + return record[index] + + return property(getter, doc=doc) + + +class CalibrationSupportAuthorityRecord(tuple): + """Immutable released proof that calibration support was valid at construction.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + support_authority_reference: str, + support_authority_digest: str, + evidence_version: int, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_owner_contract_released_at: datetime, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_authorization_receipt_released_at: datetime, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + auxiliary_authorized_from: datetime, + auxiliary_authorized_to: datetime | None, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_owner_contract_released_at: datetime, + benchmark_reference_at: datetime, + benchmark_receipt_released_at: datetime, + benchmark_receipt_superseded_at: datetime | None, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + ) -> CalibrationSupportAuthorityRecord: + """Validate identities and chronology before storing detached owner evidence.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + support_ref = _require_reference( + "support_authority_reference", + support_authority_reference, + "calibration_support_authority", + ) + support_digest = _require_digest("support_authority_digest", support_authority_digest) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + calibration_ref = _require_reference( + "calibration_receipt_reference", + calibration_receipt_reference, + "calibration_adjustment_receipt", + ) + calibration_digest = _require_digest( + "calibration_receipt_digest", calibration_receipt_digest + ) + auxiliary_authority_ref = _require_reference( + "auxiliary_authority_reference", + auxiliary_authority_reference, + "scientific_auxiliary_authority", + ) + projection_ref = _require_reference( + "auxiliary_projection_reference", + auxiliary_projection_reference, + "calibration_auxiliary_projection", + ) + projection_version = _require_positive_integer( + "auxiliary_projection_version", auxiliary_projection_version + ) + projection_digest = _require_digest( + "auxiliary_projection_digest", auxiliary_projection_digest + ) + purpose_ref = _require_reference( + "auxiliary_purpose_reference", + auxiliary_purpose_reference, + "scientific_data_use_purpose", + ) + purpose_digest = _require_digest("auxiliary_purpose_digest", auxiliary_purpose_digest) + auxiliary_owner_ref = _require_reference( + "auxiliary_owner_contract_reference", + auxiliary_owner_contract_reference, + "released_owner_contract", + ) + auxiliary_owner_version = _require_positive_integer( + "auxiliary_owner_contract_version", auxiliary_owner_contract_version + ) + auxiliary_owner_digest = _require_digest( + "auxiliary_owner_contract_digest", auxiliary_owner_contract_digest + ) + auxiliary_owner_released = _require_aware_datetime( + "auxiliary_owner_contract_released_at", auxiliary_owner_contract_released_at + ) + authorization_ref = _require_reference( + "auxiliary_authorization_receipt_reference", + auxiliary_authorization_receipt_reference, + "scientific_data_authorization", + ) + authorization_digest = _require_digest( + "auxiliary_authorization_receipt_digest", auxiliary_authorization_receipt_digest + ) + authorization_released = _require_aware_datetime( + "auxiliary_authorization_receipt_released_at", + auxiliary_authorization_receipt_released_at, + ) + use_ref = _require_reference( + "auxiliary_scientific_use_receipt_reference", + auxiliary_scientific_use_receipt_reference, + "scientific_use_receipt", + ) + use_digest = _require_digest( + "auxiliary_scientific_use_receipt_digest", auxiliary_scientific_use_receipt_digest + ) + use_at = _require_aware_datetime( + "auxiliary_scientific_use_at", auxiliary_scientific_use_at + ) + authorized_from = _require_aware_datetime( + "auxiliary_authorized_from", auxiliary_authorized_from + ) + authorized_to = ( + None + if auxiliary_authorized_to is None + else _require_aware_datetime("auxiliary_authorized_to", auxiliary_authorized_to) + ) + if auxiliary_owner_released > authorization_released: + raise ValueError("auxiliary owner contract cannot postdate authorization receipt.") + if authorization_released > use_at: + raise ValueError( + "authorization receipt must be released no later than auxiliary scientific use." + ) + if authorization_released > authorized_from: + raise ValueError( + "authorization receipt must exist before authorization begins." + ) + if authorized_to is not None and authorized_to <= authorized_from: + raise ValueError("auxiliary_authorized_to must be later than auxiliary_authorized_from.") + if use_at < authorized_from or (authorized_to is not None and use_at >= authorized_to): + raise ValueError( + "auxiliary scientific use must fall inside owner-resolved authorization interval." + ) + + benchmark_ref = _require_reference( + "benchmark_receipt_reference", + benchmark_receipt_reference, + "calibration_benchmark_receipt", + ) + benchmark_version = _require_positive_integer( + "benchmark_receipt_version", benchmark_receipt_version + ) + benchmark_digest = _require_digest("benchmark_receipt_digest", benchmark_receipt_digest) + benchmark_owner_ref = _require_reference( + "benchmark_owner_contract_reference", + benchmark_owner_contract_reference, + "released_owner_contract", + ) + benchmark_owner_version = _require_positive_integer( + "benchmark_owner_contract_version", benchmark_owner_contract_version + ) + benchmark_owner_digest = _require_digest( + "benchmark_owner_contract_digest", benchmark_owner_contract_digest + ) + benchmark_owner_released = _require_aware_datetime( + "benchmark_owner_contract_released_at", benchmark_owner_contract_released_at + ) + benchmark_reference = _require_aware_datetime( + "benchmark_reference_at", benchmark_reference_at + ) + benchmark_released = _require_aware_datetime( + "benchmark_receipt_released_at", benchmark_receipt_released_at + ) + benchmark_superseded = ( + None + if benchmark_receipt_superseded_at is None + else _require_aware_datetime( + "benchmark_receipt_superseded_at", benchmark_receipt_superseded_at + ) + ) + if benchmark_owner_released > benchmark_released: + raise ValueError("benchmark owner contract cannot postdate benchmark receipt release.") + if benchmark_superseded is not None and benchmark_superseded <= benchmark_released: + raise ValueError("benchmark supersession must be later than benchmark receipt release.") + + constructed = _require_aware_datetime("constructed_at", constructed_at) + if use_at > constructed: + raise ValueError("auxiliary scientific use cannot be later than calibration construction.") + if benchmark_reference > constructed: + raise ValueError("benchmark reference cannot be later than calibration construction.") + if benchmark_released > constructed: + raise ValueError("benchmark receipt must be released no later than calibration construction.") + if benchmark_superseded is not None and constructed >= benchmark_superseded: + raise ValueError( + "superseded benchmark cannot support calibration construction at or after cutover." + ) + + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_released = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + released = _require_aware_datetime("released_at", released_at) + if owner_released > released: + raise ValueError("owner contract cannot be released after support evidence.") + if released < constructed: + raise ValueError("support evidence cannot be released before calibration construction.") + + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + support_ref, + support_digest, + version, + calibration_ref, + calibration_digest, + auxiliary_authority_ref, + projection_ref, + projection_version, + projection_digest, + purpose_ref, + purpose_digest, + auxiliary_owner_ref, + auxiliary_owner_version, + auxiliary_owner_digest, + auxiliary_owner_released, + authorization_ref, + authorization_digest, + authorization_released, + use_ref, + use_digest, + use_at, + authorized_from, + authorized_to, + benchmark_ref, + benchmark_version, + benchmark_digest, + benchmark_owner_ref, + benchmark_owner_version, + benchmark_owner_digest, + benchmark_owner_released, + benchmark_reference, + benchmark_released, + benchmark_superseded, + constructed, + owner_ref, + owner_version, + owner_digest, + owner_released, + released, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this support authority.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity for this support authority.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + support_authority_reference = _tuple_property(2, "Return the immutable support-authority reference.") + support_authority_digest = _tuple_property(3, "Return the digest of the exact support-authority evidence.") + evidence_version = _tuple_property(4, "Return the governed support-authority evidence version.") + calibration_receipt_reference = _tuple_property(5, "Return the typed calibration receipt this support proof corroborates.") + calibration_receipt_digest = _tuple_property(6, "Return the digest of the typed calibration receipt.") + auxiliary_authority_reference = _tuple_property(7, "Return the scientific auxiliary-authority identity used by calibration.") + auxiliary_projection_reference = _tuple_property(8, "Return the purpose-limited auxiliary projection reference.") + auxiliary_projection_version = _tuple_property(9, "Return the exact auxiliary projection version.") + auxiliary_projection_digest = _tuple_property(10, "Return the digest of the auxiliary projection used by calibration.") + auxiliary_purpose_reference = _tuple_property(11, "Return the governed scientific-use purpose reference.") + auxiliary_purpose_digest = _tuple_property(12, "Return the digest of the governed scientific-use purpose.") + auxiliary_owner_contract_reference = _tuple_property(13, "Return the released auxiliary-owner contract reference.") + auxiliary_owner_contract_version = _tuple_property(14, "Return the released auxiliary-owner contract version.") + auxiliary_owner_contract_digest = _tuple_property(15, "Return the released auxiliary-owner contract digest.") + auxiliary_owner_contract_released_at = _tuple_property(16, "Return when the auxiliary-owner contract became released evidence.") + auxiliary_authorization_receipt_reference = _tuple_property(17, "Return the purpose-bound authorization receipt reference.") + auxiliary_authorization_receipt_digest = _tuple_property(18, "Return the digest of the purpose-bound authorization receipt.") + auxiliary_authorization_receipt_released_at = _tuple_property(19, "Return when the authorization receipt became released evidence.") + auxiliary_scientific_use_receipt_reference = _tuple_property(20, "Return the immutable scientific-use receipt reference.") + auxiliary_scientific_use_receipt_digest = _tuple_property(21, "Return the digest of the scientific-use receipt.") + auxiliary_scientific_use_at = _tuple_property(22, "Return the scientific-use instant committed by the calibration lineage.") + auxiliary_authorized_from = _tuple_property(23, "Return the inclusive start of the owner-resolved authorization interval.") + auxiliary_authorized_to = _tuple_property(24, "Return the optional exclusive end of the owner-resolved authorization interval.") + benchmark_receipt_reference = _tuple_property(25, "Return the calibration benchmark receipt reference.") + benchmark_receipt_version = _tuple_property(26, "Return the exact calibration benchmark receipt version.") + benchmark_receipt_digest = _tuple_property(27, "Return the digest of the exact calibration benchmark receipt.") + benchmark_owner_contract_reference = _tuple_property(28, "Return the released benchmark-owner contract reference.") + benchmark_owner_contract_version = _tuple_property(29, "Return the released benchmark-owner contract version.") + benchmark_owner_contract_digest = _tuple_property(30, "Return the released benchmark-owner contract digest.") + benchmark_owner_contract_released_at = _tuple_property(31, "Return when the benchmark-owner contract became released evidence.") + benchmark_reference_at = _tuple_property(32, "Return the benchmark reference instant committed by calibration.") + benchmark_receipt_released_at = _tuple_property(33, "Return when the benchmark receipt became released evidence.") + benchmark_receipt_superseded_at = _tuple_property(34, "Return the optional exclusive cutover that ended benchmark authority.") + constructed_at = _tuple_property(35, "Return when the typed calibration receipt was constructed.") + owner_contract_reference = _tuple_property(36, "Return the released application owner-contract reference.") + owner_contract_version = _tuple_property(37, "Return the released application owner-contract version.") + owner_contract_digest = _tuple_property(38, "Return the digest of the released application owner contract.") + owner_contract_released_at = _tuple_property(39, "Return when the application owner contract became released evidence.") + released_at = _tuple_property(40, "Return when this support proof became released application evidence.") + + +class CalibrationSupportAuthorityView: + """Sealed support evidence issued only after purpose-bound authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> CalibrationSupportAuthorityView: + """Reject direct construction so callers cannot mint reusable authority views.""" + raise TypeError( + "CalibrationSupportAuthorityView is issued only by resolve_calibration_support_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("CalibrationSupportAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("CalibrationSupportAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_calibration_support_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable corroborating support evidence without source values.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class CalibrationSupportAuthorityReadPort(Protocol): + """Owner read contract keyed only by caller-known immutable coordinates.""" + + def read_calibration_support_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> CalibrationSupportAuthorityRecord | None: + """Return matching released support evidence or ``None`` through an owner ACL.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + CalibrationSupportAuthorityReadPort, "read_calibration_support_authority" +) + + +def _caller_coordinates(record: CalibrationSupportAuthorityRecord) -> tuple[object, ...]: + """Return immutable caller-known coordinates while excluding owner chronology.""" + return ( + record.tenant_record_id, + record.validity_study_id, + record.calibration_receipt_reference, + record.calibration_receipt_digest, + record.auxiliary_authority_reference, + record.auxiliary_projection_reference, + record.auxiliary_projection_version, + record.auxiliary_projection_digest, + record.auxiliary_purpose_reference, + record.auxiliary_purpose_digest, + record.auxiliary_owner_contract_reference, + record.auxiliary_owner_contract_version, + record.auxiliary_owner_contract_digest, + record.auxiliary_authorization_receipt_reference, + record.auxiliary_authorization_receipt_digest, + record.auxiliary_scientific_use_receipt_reference, + record.auxiliary_scientific_use_receipt_digest, + record.auxiliary_scientific_use_at, + record.benchmark_receipt_reference, + record.benchmark_receipt_version, + record.benchmark_receipt_digest, + record.benchmark_owner_contract_reference, + record.benchmark_owner_contract_version, + record.benchmark_owner_contract_digest, + record.benchmark_reference_at, + record.constructed_at, + record.owner_contract_reference, + record.owner_contract_version, + record.owner_contract_digest, + ) + + +def _resolve_calibration_support_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationSupportAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve released support chronology for one calibration receipt.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static(type(read_port), "read_calibration_support_authority", None) + if type(read_capability) is not FunctionType or read_capability is _PROTOCOL_READ_CAPABILITY: + raise TypeError( + "read_port must expose a statically callable read_calibration_support_authority." + ) + + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + calibration_ref = _require_reference( + "calibration_receipt_reference", calibration_receipt_reference, "calibration_adjustment_receipt" + ) + calibration_digest = _require_digest("calibration_receipt_digest", calibration_receipt_digest) + auxiliary_authority_ref = _require_reference( + "auxiliary_authority_reference", auxiliary_authority_reference, "scientific_auxiliary_authority" + ) + projection_ref = _require_reference( + "auxiliary_projection_reference", auxiliary_projection_reference, "calibration_auxiliary_projection" + ) + projection_version = _require_positive_integer( + "auxiliary_projection_version", auxiliary_projection_version + ) + projection_digest = _require_digest("auxiliary_projection_digest", auxiliary_projection_digest) + purpose_ref = _require_reference( + "auxiliary_purpose_reference", auxiliary_purpose_reference, "scientific_data_use_purpose" + ) + purpose_digest = _require_digest("auxiliary_purpose_digest", auxiliary_purpose_digest) + auxiliary_owner_ref = _require_reference( + "auxiliary_owner_contract_reference", auxiliary_owner_contract_reference, "released_owner_contract" + ) + auxiliary_owner_version = _require_positive_integer( + "auxiliary_owner_contract_version", auxiliary_owner_contract_version + ) + auxiliary_owner_digest = _require_digest( + "auxiliary_owner_contract_digest", auxiliary_owner_contract_digest + ) + authorization_ref = _require_reference( + "auxiliary_authorization_receipt_reference", + auxiliary_authorization_receipt_reference, + "scientific_data_authorization", + ) + authorization_digest = _require_digest( + "auxiliary_authorization_receipt_digest", auxiliary_authorization_receipt_digest + ) + scientific_use_ref = _require_reference( + "auxiliary_scientific_use_receipt_reference", + auxiliary_scientific_use_receipt_reference, + "scientific_use_receipt", + ) + scientific_use_digest = _require_digest( + "auxiliary_scientific_use_receipt_digest", auxiliary_scientific_use_receipt_digest + ) + scientific_use_at = _require_aware_datetime( + "auxiliary_scientific_use_at", auxiliary_scientific_use_at + ) + benchmark_ref = _require_reference( + "benchmark_receipt_reference", benchmark_receipt_reference, "calibration_benchmark_receipt" + ) + benchmark_version = _require_positive_integer("benchmark_receipt_version", benchmark_receipt_version) + benchmark_digest = _require_digest("benchmark_receipt_digest", benchmark_receipt_digest) + benchmark_owner_ref = _require_reference( + "benchmark_owner_contract_reference", benchmark_owner_contract_reference, "released_owner_contract" + ) + benchmark_owner_version = _require_positive_integer( + "benchmark_owner_contract_version", benchmark_owner_contract_version + ) + benchmark_owner_digest = _require_digest( + "benchmark_owner_contract_digest", benchmark_owner_contract_digest + ) + benchmark_reference = _require_aware_datetime("benchmark_reference_at", benchmark_reference_at) + constructed = _require_aware_datetime("constructed_at", constructed_at) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + calibration_receipt_reference=calibration_ref, + calibration_receipt_digest=calibration_digest, + auxiliary_authority_reference=auxiliary_authority_ref, + auxiliary_projection_reference=projection_ref, + auxiliary_projection_version=projection_version, + auxiliary_projection_digest=projection_digest, + auxiliary_purpose_reference=purpose_ref, + auxiliary_purpose_digest=purpose_digest, + auxiliary_owner_contract_reference=auxiliary_owner_ref, + auxiliary_owner_contract_version=auxiliary_owner_version, + auxiliary_owner_contract_digest=auxiliary_owner_digest, + auxiliary_authorization_receipt_reference=authorization_ref, + auxiliary_authorization_receipt_digest=authorization_digest, + auxiliary_scientific_use_receipt_reference=scientific_use_ref, + auxiliary_scientific_use_receipt_digest=scientific_use_digest, + auxiliary_scientific_use_at=scientific_use_at, + benchmark_receipt_reference=benchmark_ref, + benchmark_receipt_version=benchmark_version, + benchmark_receipt_digest=benchmark_digest, + benchmark_owner_contract_reference=benchmark_owner_ref, + benchmark_owner_contract_version=benchmark_owner_version, + benchmark_owner_contract_digest=benchmark_owner_digest, + benchmark_reference_at=benchmark_reference, + constructed_at=constructed, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise CalibrationSupportAuthorityNotFound(str(study_id)) + if type(persisted) is not CalibrationSupportAuthorityRecord: + raise CalibrationSupportAuthorityIntegrityError( + "owner port returned non-canonical calibration support authority evidence" + ) + + try: + record = CalibrationSupportAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + support_authority_reference=persisted.support_authority_reference, + support_authority_digest=persisted.support_authority_digest, + evidence_version=persisted.evidence_version, + calibration_receipt_reference=persisted.calibration_receipt_reference, + calibration_receipt_digest=persisted.calibration_receipt_digest, + auxiliary_authority_reference=persisted.auxiliary_authority_reference, + auxiliary_projection_reference=persisted.auxiliary_projection_reference, + auxiliary_projection_version=persisted.auxiliary_projection_version, + auxiliary_projection_digest=persisted.auxiliary_projection_digest, + auxiliary_purpose_reference=persisted.auxiliary_purpose_reference, + auxiliary_purpose_digest=persisted.auxiliary_purpose_digest, + auxiliary_owner_contract_reference=persisted.auxiliary_owner_contract_reference, + auxiliary_owner_contract_version=persisted.auxiliary_owner_contract_version, + auxiliary_owner_contract_digest=persisted.auxiliary_owner_contract_digest, + auxiliary_owner_contract_released_at=persisted.auxiliary_owner_contract_released_at, + auxiliary_authorization_receipt_reference=persisted.auxiliary_authorization_receipt_reference, + auxiliary_authorization_receipt_digest=persisted.auxiliary_authorization_receipt_digest, + auxiliary_authorization_receipt_released_at=persisted.auxiliary_authorization_receipt_released_at, + auxiliary_scientific_use_receipt_reference=persisted.auxiliary_scientific_use_receipt_reference, + auxiliary_scientific_use_receipt_digest=persisted.auxiliary_scientific_use_receipt_digest, + auxiliary_scientific_use_at=persisted.auxiliary_scientific_use_at, + auxiliary_authorized_from=persisted.auxiliary_authorized_from, + auxiliary_authorized_to=persisted.auxiliary_authorized_to, + benchmark_receipt_reference=persisted.benchmark_receipt_reference, + benchmark_receipt_version=persisted.benchmark_receipt_version, + benchmark_receipt_digest=persisted.benchmark_receipt_digest, + benchmark_owner_contract_reference=persisted.benchmark_owner_contract_reference, + benchmark_owner_contract_version=persisted.benchmark_owner_contract_version, + benchmark_owner_contract_digest=persisted.benchmark_owner_contract_digest, + benchmark_owner_contract_released_at=persisted.benchmark_owner_contract_released_at, + benchmark_reference_at=persisted.benchmark_reference_at, + benchmark_receipt_released_at=persisted.benchmark_receipt_released_at, + benchmark_receipt_superseded_at=persisted.benchmark_receipt_superseded_at, + constructed_at=persisted.constructed_at, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise CalibrationSupportAuthorityIntegrityError( + "owner port returned structurally invalid calibration support authority evidence" + ) from exc + if record != persisted: + raise CalibrationSupportAuthorityIntegrityError( + "owner port returned non-canonical calibration support authority structure" + ) + + expected = ( + tenant_id, + study_id, + calibration_ref, + calibration_digest, + auxiliary_authority_ref, + projection_ref, + projection_version, + projection_digest, + purpose_ref, + purpose_digest, + auxiliary_owner_ref, + auxiliary_owner_version, + auxiliary_owner_digest, + authorization_ref, + authorization_digest, + scientific_use_ref, + scientific_use_digest, + scientific_use_at, + benchmark_ref, + benchmark_version, + benchmark_digest, + benchmark_owner_ref, + benchmark_owner_version, + benchmark_owner_digest, + benchmark_reference, + constructed, + owner_ref, + owner_version, + owner_digest, + ) + if _caller_coordinates(record) != expected: + raise CalibrationSupportAuthorityIntegrityError( + "released calibration support authority does not match requested coordinates" + ) + if record.released_at > use_instant: + raise CalibrationSupportAuthorityIntegrityError( + "calibration support evidence must be released before scientific use" + ) + + values = {field_name: getattr(record, field_name) for field_name in _READ_FIELDS} + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return tenant_identity, study_identity, fields + + +def _build_calibration_support_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: CalibrationSupportAuthorityView) -> None: + """Verify one support view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise CalibrationSupportAuthorityIntegrityError( + "calibration support view was not issued by " + "resolve_calibration_support_authority" + ) from exc + if marker is not issuance_marker: + raise CalibrationSupportAuthorityIntegrityError( + "calibration support view was not issued by " + "resolve_calibration_support_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + calibration_receipt_reference: str, + calibration_receipt_digest: str, + auxiliary_authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + auxiliary_purpose_reference: str, + auxiliary_purpose_digest: str, + auxiliary_owner_contract_reference: str, + auxiliary_owner_contract_version: int, + auxiliary_owner_contract_digest: str, + auxiliary_authorization_receipt_reference: str, + auxiliary_authorization_receipt_digest: str, + auxiliary_scientific_use_receipt_reference: str, + auxiliary_scientific_use_receipt_digest: str, + auxiliary_scientific_use_at: datetime, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationSupportAuthorityReadPort, + ) -> CalibrationSupportAuthorityView: + """Authorize and resolve released support chronology for one calibration receipt.""" + tenant_identity, study_identity, fields = ( + _resolve_calibration_support_authority_state( + **{ + name: value + for name, value in locals().items() + if name != "issuance_marker" + } + ) + ) + view = object.__new__(CalibrationSupportAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_calibration_support_view_issued, + resolve_calibration_support_authority, +) = _build_calibration_support_view_runtime() +del _build_calibration_support_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_authority.py new file mode 100644 index 000000000..fcab8ec96 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_authority.py @@ -0,0 +1,985 @@ +"""Corroborate the complete released final analysis-weight construction. + +This application boundary verifies the scientific coordinates needed to reproduce +one final point-estimation weight receipt without importing mutable validity- +analysis source, copying row-level weights, or querying foreign application +tables. Durable persistence remains the responsibility of the owner adapter. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "final_analysis_weight_authority" +_OPERATION = "read" +_WEIGHT_SCOPE_CODES = frozenset({"cross_sectional", "longitudinal"}) +_SPECIALIZED_EVIDENCE_KIND_BY_ADJUSTMENT_CODE = { + "nonresponse_adjustment": "nonresponse_adjustment_receipt", + "calibration_adjustment": "calibration_adjustment_receipt", + "raking_adjustment": "calibration_adjustment_receipt", + "poststratification_adjustment": "calibration_adjustment_receipt", + "weight_trimming_adjustment": "trimming_bounding_adjustment_receipt", + "weight_bounding_adjustment": "trimming_bounding_adjustment_receipt", + "weight_winsorization_adjustment": "trimming_bounding_adjustment_receipt", +} +_READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "estimand_reference", + "estimand_digest", + "estimand_scope_code", + "target_population_reference", + "target_population_digest", + "analysis_unit_code", + "analysis_window_reference", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "analytic_case_occurrence_set_digest", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_evidence_digest", + "base_weight_artifact_digest", + "adjustments", + "final_weight_artifact_digest", + "weight_eligibility_receipt_reference", + "weight_eligibility_receipt_digest", + "analytic_case_count", + "constructed_at", + "correction_sequence", + "supersedes_receipt_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class FinalAnalysisWeightAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the final-weight receipt.""" + + +class FinalAnalysisWeightAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence cannot corroborate the requested final-weight lineage.""" + + +def _require_weight_scope(value: object) -> str: + """Require explicit cross-sectional or longitudinal estimand semantics.""" + if type(value) is not str or value not in _WEIGHT_SCOPE_CODES: + raise ValueError("estimand_scope_code must be cross_sectional or longitudinal.") + return value + + +class FinalWeightAdjustmentCoordinate(tuple): + """Immutable, value-minimized coordinate for one ordered weight transform.""" + + __slots__ = () + + def __new__( + cls, + *, + sequence_number: int, + adjustment_code: str, + method_reference: str, + method_version: int, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + configuration_digest: str, + evidence_receipt_digest: str, + evidence_kind: str, + ) -> FinalWeightAdjustmentCoordinate: + """Validate one transform without storing case-level weight values.""" + sequence = _require_positive_integer("sequence_number", sequence_number) + code = _require_code("adjustment_code", adjustment_code) + method_ref = _require_reference("method_reference", method_reference, "weight_method") + method_ver = _require_positive_integer("method_version", method_version) + input_digest = _require_digest( + "input_weight_artifact_digest", input_weight_artifact_digest + ) + output_digest = _require_digest( + "output_weight_artifact_digest", output_weight_artifact_digest + ) + config_digest = _require_digest("configuration_digest", configuration_digest) + evidence_digest = _require_digest("evidence_receipt_digest", evidence_receipt_digest) + kind = _require_code("evidence_kind", evidence_kind) + required_kind = _SPECIALIZED_EVIDENCE_KIND_BY_ADJUSTMENT_CODE.get(code) + if required_kind is None: + raise ValueError( + "governed adjustment_code must identify released owner evidence." + ) + if kind != required_kind: + raise ValueError(f"{code} requires evidence_kind {required_kind}.") + if input_digest == output_digest: + raise ValueError( + "output_weight_artifact_digest must identify the transformed weight artifact." + ) + return tuple.__new__( + cls, + ( + sequence, + code, + method_ref, + method_ver, + input_digest, + output_digest, + config_digest, + evidence_digest, + kind, + ), + ) + + @property + def sequence_number(self) -> int: + """Return the one-based transform order.""" + return self[0] + + @property + def adjustment_code(self) -> str: + """Return the controlled adjustment code.""" + return self[1] + + @property + def method_reference(self) -> str: + """Return the controlled weight-method reference.""" + return self[2] + + @property + def method_version(self) -> int: + """Return the positive weight-method version.""" + return self[3] + + @property + def input_weight_artifact_digest(self) -> str: + """Return the transform input artifact digest.""" + return self[4] + + @property + def output_weight_artifact_digest(self) -> str: + """Return the transform output artifact digest.""" + return self[5] + + @property + def configuration_digest(self) -> str: + """Return the immutable transform configuration digest.""" + return self[6] + + @property + def evidence_receipt_digest(self) -> str: + """Return the immutable supporting evidence-receipt digest.""" + return self[7] + + @property + def evidence_kind(self) -> str: + """Return the typed supporting evidence kind.""" + return self[8] + + +class FinalAnalysisWeightAuthorityRecord(tuple): + """Immutable owner projection for one complete released final-weight receipt.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + estimand_reference: str, + estimand_digest: str, + estimand_scope_code: str, + target_population_reference: str, + target_population_digest: str, + analysis_unit_code: str, + analysis_window_reference: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + analytic_case_occurrence_set_digest: str, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_evidence_digest: str, + base_weight_artifact_digest: str, + adjustments: tuple[FinalWeightAdjustmentCoordinate, ...], + final_weight_artifact_digest: str, + weight_eligibility_receipt_reference: str, + weight_eligibility_receipt_digest: str, + analytic_case_count: int, + constructed_at: datetime, + correction_sequence: int, + supersedes_receipt_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> FinalAnalysisWeightAuthorityRecord: + """Validate and detach the complete scientific point-weight lineage.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "analysis_weight_receipt_reference", + analysis_weight_receipt_reference, + "analysis_weight_receipt", + ) + receipt_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + estimand_ref = _require_reference( + "estimand_reference", estimand_reference, "validation_estimand" + ) + estimand_evidence = _require_digest("estimand_digest", estimand_digest) + scope = _require_weight_scope(estimand_scope_code) + target_ref = _require_reference( + "target_population_reference", + target_population_reference, + "analysis_target_population", + ) + target_digest = _require_digest( + "target_population_digest", target_population_digest + ) + unit_code = _require_code("analysis_unit_code", analysis_unit_code) + window_ref = _require_reference( + "analysis_window_reference", analysis_window_reference, "analysis_window" + ) + duration_ref = _require_reference( + "reference_duration_reference", + reference_duration_reference, + "analysis_reference_duration", + ) + duration_digest = _require_digest( + "reference_duration_digest", reference_duration_digest + ) + eligible_digest = _require_digest( + "eligible_case_set_digest", eligible_case_set_digest + ) + analytic_digest = _require_digest( + "analytic_case_occurrence_set_digest", analytic_case_occurrence_set_digest + ) + source_ref = _require_reference( + "source_universe_receipt_reference", + source_universe_receipt_reference, + "source_universe_receipt", + ) + source_version = _require_positive_integer( + "source_universe_receipt_version", source_universe_receipt_version + ) + source_digest = _require_digest( + "source_universe_receipt_digest", source_universe_receipt_digest + ) + sampling_ref = _require_reference( + "sampling_design_receipt_reference", + sampling_design_receipt_reference, + "sampling_design_receipt", + ) + sampling_version = _require_positive_integer( + "sampling_design_receipt_version", sampling_design_receipt_version + ) + sampling_digest = _require_digest( + "sampling_design_receipt_digest", sampling_design_receipt_digest + ) + base_method = _require_code("base_weight_method_code", base_weight_method_code) + base_method_version_value = _require_positive_integer( + "base_weight_method_version", base_weight_method_version + ) + base_evidence = _require_digest( + "base_weight_evidence_digest", base_weight_evidence_digest + ) + base_artifact = _require_digest( + "base_weight_artifact_digest", base_weight_artifact_digest + ) + if type(adjustments) is not tuple: + raise ValueError("adjustments must be an immutable tuple.") + detached_adjustments: list[FinalWeightAdjustmentCoordinate] = [] + expected_input = base_artifact + for expected_sequence, adjustment in enumerate(adjustments, start=1): + if type(adjustment) is not FinalWeightAdjustmentCoordinate: + raise ValueError( + "adjustments must contain exact FinalWeightAdjustmentCoordinate values." + ) + if adjustment.sequence_number != expected_sequence: + raise ValueError("adjustments must have contiguous sequence_number values.") + if adjustment.input_weight_artifact_digest != expected_input: + raise ValueError( + "adjustment input_weight_artifact_digest breaks the weight chain." + ) + detached = FinalWeightAdjustmentCoordinate( + sequence_number=adjustment.sequence_number, + adjustment_code=adjustment.adjustment_code, + method_reference=adjustment.method_reference, + method_version=adjustment.method_version, + input_weight_artifact_digest=adjustment.input_weight_artifact_digest, + output_weight_artifact_digest=adjustment.output_weight_artifact_digest, + configuration_digest=adjustment.configuration_digest, + evidence_receipt_digest=adjustment.evidence_receipt_digest, + evidence_kind=adjustment.evidence_kind, + ) + detached_adjustments.append(detached) + expected_input = detached.output_weight_artifact_digest + final_artifact = _require_digest( + "final_weight_artifact_digest", final_weight_artifact_digest + ) + if expected_input != final_artifact: + raise ValueError( + "final_weight_artifact_digest must equal the ordered adjustment chain output." + ) + eligibility_ref = _require_reference( + "weight_eligibility_receipt_reference", + weight_eligibility_receipt_reference, + "weight_eligibility_receipt", + ) + eligibility_digest = _require_digest( + "weight_eligibility_receipt_digest", weight_eligibility_receipt_digest + ) + case_count = _require_positive_integer("analytic_case_count", analytic_case_count) + constructed = _require_aware_datetime("constructed_at", constructed_at) + correction = _require_positive_integer("correction_sequence", correction_sequence) + supersedes_digest: str | None + if correction == 1: + if supersedes_receipt_digest is not None: + raise ValueError( + "supersedes_receipt_digest must be absent for correction_sequence 1." + ) + supersedes_digest = None + else: + if supersedes_receipt_digest is None: + raise ValueError( + "supersedes_receipt_digest is required when correction_sequence exceeds 1." + ) + supersedes_digest = _require_digest( + "supersedes_receipt_digest", supersedes_receipt_digest + ) + if supersedes_digest == receipt_digest: + raise ValueError("a final analysis-weight receipt cannot supersede itself.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release_instant = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if release_instant < constructed: + raise ValueError("released_at cannot precede constructed_at.") + if owner_release_instant > release_instant: + raise ValueError( + "owner contract must be released no later than the final analysis-weight authority" + ) + supersession_instant = None + if superseded_at is not None: + supersession_instant = _require_aware_datetime("superseded_at", superseded_at) + if supersession_instant <= release_instant: + raise ValueError("superseded_at must be later than released_at") + + fields: tuple[tuple[str, object], ...] = ( + ("adjustments", tuple(detached_adjustments)), + ("analysis_unit_code", unit_code), + ("analysis_weight_receipt_digest", receipt_digest), + ("analysis_weight_receipt_reference", receipt_ref), + ("analysis_window_reference", window_ref), + ("analytic_case_count", case_count), + ("analytic_case_occurrence_set_digest", analytic_digest), + ("base_weight_artifact_digest", base_artifact), + ("base_weight_evidence_digest", base_evidence), + ("base_weight_method_code", base_method), + ("base_weight_method_version", base_method_version_value), + ("constructed_at", constructed), + ("correction_sequence", correction), + ("eligible_case_set_digest", eligible_digest), + ("estimand_digest", estimand_evidence), + ("estimand_reference", estimand_ref), + ("estimand_scope_code", scope), + ("evidence_version", version), + ("final_weight_artifact_digest", final_artifact), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_version", owner_version), + ("reference_duration_digest", duration_digest), + ("reference_duration_reference", duration_ref), + ("sampling_design_receipt_digest", sampling_digest), + ("sampling_design_receipt_reference", sampling_ref), + ("sampling_design_receipt_version", sampling_version), + ("source_universe_receipt_digest", source_digest), + ("source_universe_receipt_reference", source_ref), + ("source_universe_receipt_version", source_version), + ("supersedes_receipt_digest", supersedes_digest), + ("target_population_digest", target_digest), + ("target_population_reference", target_ref), + ("weight_eligibility_receipt_digest", eligibility_digest), + ("weight_eligibility_receipt_reference", eligibility_ref), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + fields, + release_instant, + owner_release_instant, + supersession_instant, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable scientific coordinates without row-level weights.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return the owner-resolved release instant.""" + return self[3] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the governing owner contract became released authority.""" + return self[4] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive owner-resolved cutover instant, when one exists.""" + return self[5] + + +class FinalAnalysisWeightAuthorityView: + """Sealed field-minimized final-weight evidence issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> FinalAnalysisWeightAuthorityView: + """Reject public construction; the resolver is the only supported issuer.""" + raise TypeError( + "FinalAnalysisWeightAuthorityView is issued only by " + "resolve_final_analysis_weight_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("FinalAnalysisWeightAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("FinalAnalysisWeightAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_final_analysis_weight_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable final-weight provenance.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class FinalAnalysisWeightAuthorityReadPort(Protocol): + """Owner read contract for one released final analysis-weight receipt.""" + + def read_final_analysis_weight_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + estimand_reference: str, + estimand_digest: str, + estimand_scope_code: str, + target_population_reference: str, + target_population_digest: str, + analysis_unit_code: str, + analysis_window_reference: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + analytic_case_occurrence_set_digest: str, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_evidence_digest: str, + base_weight_artifact_digest: str, + adjustments: tuple[FinalWeightAdjustmentCoordinate, ...], + final_weight_artifact_digest: str, + weight_eligibility_receipt_reference: str, + weight_eligibility_receipt_digest: str, + analytic_case_count: int, + constructed_at: datetime, + correction_sequence: int, + supersedes_receipt_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> FinalAnalysisWeightAuthorityRecord | None: + """Return matching released final-weight evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + FinalAnalysisWeightAuthorityReadPort, "read_final_analysis_weight_authority" +) + + +def _resolve_final_analysis_weight_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + estimand_reference: str, + estimand_digest: str, + estimand_scope_code: str, + target_population_reference: str, + target_population_digest: str, + analysis_unit_code: str, + analysis_window_reference: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + analytic_case_occurrence_set_digest: str, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_evidence_digest: str, + base_weight_artifact_digest: str, + adjustments: tuple[FinalWeightAdjustmentCoordinate, ...], + final_weight_artifact_digest: str, + weight_eligibility_receipt_reference: str, + weight_eligibility_receipt_digest: str, + analytic_case_count: int, + constructed_at: datetime, + correction_sequence: int, + supersedes_receipt_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalAnalysisWeightAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize then corroborate the complete released final-weight lineage.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_final_analysis_weight_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_final_analysis_weight_authority." + ) + + requested = FinalAnalysisWeightAuthorityRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + analysis_weight_receipt_reference=analysis_weight_receipt_reference, + analysis_weight_receipt_digest=analysis_weight_receipt_digest, + evidence_version=evidence_version, + estimand_reference=estimand_reference, + estimand_digest=estimand_digest, + estimand_scope_code=estimand_scope_code, + target_population_reference=target_population_reference, + target_population_digest=target_population_digest, + analysis_unit_code=analysis_unit_code, + analysis_window_reference=analysis_window_reference, + reference_duration_reference=reference_duration_reference, + reference_duration_digest=reference_duration_digest, + eligible_case_set_digest=eligible_case_set_digest, + analytic_case_occurrence_set_digest=analytic_case_occurrence_set_digest, + source_universe_receipt_reference=source_universe_receipt_reference, + source_universe_receipt_version=source_universe_receipt_version, + source_universe_receipt_digest=source_universe_receipt_digest, + sampling_design_receipt_reference=sampling_design_receipt_reference, + sampling_design_receipt_version=sampling_design_receipt_version, + sampling_design_receipt_digest=sampling_design_receipt_digest, + base_weight_method_code=base_weight_method_code, + base_weight_method_version=base_weight_method_version, + base_weight_evidence_digest=base_weight_evidence_digest, + base_weight_artifact_digest=base_weight_artifact_digest, + adjustments=adjustments, + final_weight_artifact_digest=final_weight_artifact_digest, + weight_eligibility_receipt_reference=weight_eligibility_receipt_reference, + weight_eligibility_receipt_digest=weight_eligibility_receipt_digest, + analytic_case_count=analytic_case_count, + constructed_at=constructed_at, + correction_sequence=correction_sequence, + supersedes_receipt_digest=supersedes_receipt_digest, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + owner_contract_released_at=constructed_at, + released_at=constructed_at, + superseded_at=None, + ) + tenant_id = requested.tenant_record_id + study_id = requested.validity_study_id + requested_values = dict(requested.fields) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=requested.tenant_record_id, + validity_study_id=requested.validity_study_id, + analysis_weight_receipt_reference=requested_values[ + "analysis_weight_receipt_reference" + ], + analysis_weight_receipt_digest=requested_values[ + "analysis_weight_receipt_digest" + ], + evidence_version=requested_values["evidence_version"], + estimand_reference=requested_values["estimand_reference"], + estimand_digest=requested_values["estimand_digest"], + estimand_scope_code=requested_values["estimand_scope_code"], + target_population_reference=requested_values["target_population_reference"], + target_population_digest=requested_values["target_population_digest"], + analysis_unit_code=requested_values["analysis_unit_code"], + analysis_window_reference=requested_values["analysis_window_reference"], + reference_duration_reference=requested_values["reference_duration_reference"], + reference_duration_digest=requested_values["reference_duration_digest"], + eligible_case_set_digest=requested_values["eligible_case_set_digest"], + analytic_case_occurrence_set_digest=requested_values[ + "analytic_case_occurrence_set_digest" + ], + source_universe_receipt_reference=requested_values[ + "source_universe_receipt_reference" + ], + source_universe_receipt_version=requested_values[ + "source_universe_receipt_version" + ], + source_universe_receipt_digest=requested_values[ + "source_universe_receipt_digest" + ], + sampling_design_receipt_reference=requested_values[ + "sampling_design_receipt_reference" + ], + sampling_design_receipt_version=requested_values[ + "sampling_design_receipt_version" + ], + sampling_design_receipt_digest=requested_values[ + "sampling_design_receipt_digest" + ], + base_weight_method_code=requested_values["base_weight_method_code"], + base_weight_method_version=requested_values["base_weight_method_version"], + base_weight_evidence_digest=requested_values["base_weight_evidence_digest"], + base_weight_artifact_digest=requested_values["base_weight_artifact_digest"], + adjustments=requested_values["adjustments"], + final_weight_artifact_digest=requested_values["final_weight_artifact_digest"], + weight_eligibility_receipt_reference=requested_values[ + "weight_eligibility_receipt_reference" + ], + weight_eligibility_receipt_digest=requested_values[ + "weight_eligibility_receipt_digest" + ], + analytic_case_count=requested_values["analytic_case_count"], + constructed_at=requested_values["constructed_at"], + correction_sequence=requested_values["correction_sequence"], + supersedes_receipt_digest=requested_values["supersedes_receipt_digest"], + owner_contract_reference=requested_values["owner_contract_reference"], + owner_contract_version=requested_values["owner_contract_version"], + owner_contract_digest=requested_values["owner_contract_digest"], + ) + if persisted is None: + raise FinalAnalysisWeightAuthorityNotFound(str(study_id)) + if type(persisted) is not FinalAnalysisWeightAuthorityRecord: + raise FinalAnalysisWeightAuthorityIntegrityError( + "owner port returned non-canonical final analysis-weight authority evidence" + ) + + try: + record = FinalAnalysisWeightAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise FinalAnalysisWeightAuthorityIntegrityError( + "owner port returned malformed final analysis-weight authority evidence" + ) from exc + if record != persisted: + raise FinalAnalysisWeightAuthorityIntegrityError( + "owner port returned non-canonical final analysis-weight authority evidence" + ) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", requested.tenant_record_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", requested.validity_study_id) + or record.fields != requested.fields + ): + raise FinalAnalysisWeightAuthorityIntegrityError( + "released final analysis-weight authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise FinalAnalysisWeightAuthorityIntegrityError( + "final analysis-weight authority cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise FinalAnalysisWeightAuthorityIntegrityError( + "final analysis-weight authority cannot be used at or after supersession" + ) + + values = dict(record.fields) + values["owner_contract_released_at"] = record.owner_contract_released_at + values["released_at"] = record.released_at + values["superseded_at"] = record.superseded_at + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_final_analysis_weight_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: FinalAnalysisWeightAuthorityView) -> None: + """Verify one final analysis-weight view against the private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise FinalAnalysisWeightAuthorityIntegrityError( + "final analysis-weight view was not issued by " + "resolve_final_analysis_weight_authority" + ) from exc + if marker is not issuance_marker: + raise FinalAnalysisWeightAuthorityIntegrityError( + "final analysis-weight view was not issued by " + "resolve_final_analysis_weight_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + estimand_reference: str, + estimand_digest: str, + estimand_scope_code: str, + target_population_reference: str, + target_population_digest: str, + analysis_unit_code: str, + analysis_window_reference: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + analytic_case_occurrence_set_digest: str, + source_universe_receipt_reference: str, + source_universe_receipt_version: int, + source_universe_receipt_digest: str, + sampling_design_receipt_reference: str, + sampling_design_receipt_version: int, + sampling_design_receipt_digest: str, + base_weight_method_code: str, + base_weight_method_version: int, + base_weight_evidence_digest: str, + base_weight_artifact_digest: str, + adjustments: tuple[FinalWeightAdjustmentCoordinate, ...], + final_weight_artifact_digest: str, + weight_eligibility_receipt_reference: str, + weight_eligibility_receipt_digest: str, + analytic_case_count: int, + constructed_at: datetime, + correction_sequence: int, + supersedes_receipt_digest: str | None, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalAnalysisWeightAuthorityReadPort, + ) -> FinalAnalysisWeightAuthorityView: + """Authorize then issue the complete released final-weight projection.""" + tenant_identity, study_identity, fields = ( + _resolve_final_analysis_weight_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + analysis_weight_receipt_reference=analysis_weight_receipt_reference, + analysis_weight_receipt_digest=analysis_weight_receipt_digest, + evidence_version=evidence_version, + estimand_reference=estimand_reference, + estimand_digest=estimand_digest, + estimand_scope_code=estimand_scope_code, + target_population_reference=target_population_reference, + target_population_digest=target_population_digest, + analysis_unit_code=analysis_unit_code, + analysis_window_reference=analysis_window_reference, + reference_duration_reference=reference_duration_reference, + reference_duration_digest=reference_duration_digest, + eligible_case_set_digest=eligible_case_set_digest, + analytic_case_occurrence_set_digest=analytic_case_occurrence_set_digest, + source_universe_receipt_reference=source_universe_receipt_reference, + source_universe_receipt_version=source_universe_receipt_version, + source_universe_receipt_digest=source_universe_receipt_digest, + sampling_design_receipt_reference=sampling_design_receipt_reference, + sampling_design_receipt_version=sampling_design_receipt_version, + sampling_design_receipt_digest=sampling_design_receipt_digest, + base_weight_method_code=base_weight_method_code, + base_weight_method_version=base_weight_method_version, + base_weight_evidence_digest=base_weight_evidence_digest, + base_weight_artifact_digest=base_weight_artifact_digest, + adjustments=adjustments, + final_weight_artifact_digest=final_weight_artifact_digest, + weight_eligibility_receipt_reference=weight_eligibility_receipt_reference, + weight_eligibility_receipt_digest=weight_eligibility_receipt_digest, + analytic_case_count=analytic_case_count, + constructed_at=constructed_at, + correction_sequence=correction_sequence, + supersedes_receipt_digest=supersedes_receipt_digest, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(FinalAnalysisWeightAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_final_analysis_weight_view_issued, + resolve_final_analysis_weight_authority, +) = _build_final_analysis_weight_view_runtime() +del _build_final_analysis_weight_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_component_binding_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_component_binding_authority.py new file mode 100644 index 000000000..09c4aee2f --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_component_binding_authority.py @@ -0,0 +1,568 @@ +"""Resolve exact typed component receipts behind one released final analysis weight. + +The v1 final-weight receipt already commits component evidence digests, but a +digest alone is not an owner-record locator. This companion authority maps one +immutable final-weight receipt identity to the exact released base-weight and +specialized adjustment receipts needed for deterministic reproduction. It does +not copy row-level weights or foreign application-table values. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "final_weight_component_binding_authority" +_OPERATION = "read" +_EVIDENCE_REFERENCE_NAMESPACE_BY_KIND = { + "nonresponse_adjustment_receipt": "nonresponse_adjustment_receipt", + "calibration_adjustment_receipt": "calibration_adjustment_receipt", + "trimming_bounding_adjustment_receipt": "trimming_bounding_adjustment_receipt", +} +_READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "analysis_weight_evidence_version", + "binding_reference", + "binding_digest", + "binding_version", + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "base_weight_evidence_version", + "adjustment_bindings", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class FinalWeightComponentBindingAuthorityNotFound(LookupError): + """Indicate that no released component locator exists for the final-weight receipt.""" + + +class FinalWeightComponentBindingAuthorityIntegrityError(RuntimeError): + """Indicate that component-binding owner evidence is malformed or targets another receipt.""" + + +class FinalWeightAdjustmentEvidenceBinding(tuple): + """Exact released receipt identity for one governed specialized adjustment.""" + + __slots__ = () + + def __new__( + cls, + *, + sequence_number: int, + evidence_kind: str, + evidence_receipt_reference: str, + evidence_version: int, + evidence_receipt_digest: str, + ) -> FinalWeightAdjustmentEvidenceBinding: + """Validate a specialized receipt locator without copying adjustment values.""" + sequence = _require_positive_integer("sequence_number", sequence_number) + kind = _require_code("evidence_kind", evidence_kind) + namespace = _EVIDENCE_REFERENCE_NAMESPACE_BY_KIND.get(kind) + if namespace is None: + raise ValueError("evidence_kind must identify a governed specialized receipt.") + receipt_reference = _require_reference( + "evidence_receipt_reference", evidence_receipt_reference, namespace + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1 for specialized adjustment receipts.") + receipt_digest = _require_digest("evidence_receipt_digest", evidence_receipt_digest) + return tuple.__new__( + cls, + (sequence, kind, receipt_reference, version, receipt_digest), + ) + + @property + def sequence_number(self) -> int: + """Return the adjustment sequence from the final-weight construction.""" + return self[0] + + @property + def evidence_kind(self) -> str: + """Return the governed specialized receipt family.""" + return self[1] + + @property + def evidence_receipt_reference(self) -> str: + """Return the exact immutable specialized receipt reference.""" + return self[2] + + @property + def evidence_version(self) -> int: + """Return the governed specialized receipt evidence version.""" + return self[3] + + @property + def evidence_receipt_digest(self) -> str: + """Return the immutable specialized receipt digest.""" + return self[4] + + +class FinalWeightComponentBindingAuthorityRecord(tuple): + """Immutable owner locator from one final-weight receipt to typed component receipts.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + analysis_weight_evidence_version: int, + binding_reference: str, + binding_digest: str, + binding_version: int, + base_weight_evidence_receipt_reference: str, + base_weight_evidence_receipt_digest: str, + base_weight_evidence_version: int, + adjustment_bindings: tuple[FinalWeightAdjustmentEvidenceBinding, ...], + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> FinalWeightComponentBindingAuthorityRecord: + """Validate immutable receipt locators and owner-resolved authority chronology.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + final_receipt_reference = _require_reference( + "analysis_weight_receipt_reference", + analysis_weight_receipt_reference, + "analysis_weight_receipt", + ) + final_receipt_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + final_version = _require_positive_integer( + "analysis_weight_evidence_version", analysis_weight_evidence_version + ) + if final_version != 1: + raise ValueError("analysis_weight_evidence_version must remain 1.") + locator_reference = _require_reference( + "binding_reference", + binding_reference, + "final_weight_component_binding", + ) + locator_digest = _require_digest("binding_digest", binding_digest) + locator_version = _require_positive_integer("binding_version", binding_version) + if locator_version != 1: + raise ValueError("binding_version must remain 1.") + base_receipt_reference = _require_reference( + "base_weight_evidence_receipt_reference", + base_weight_evidence_receipt_reference, + "base_weight_evidence_receipt", + ) + base_receipt_digest = _require_digest( + "base_weight_evidence_receipt_digest", + base_weight_evidence_receipt_digest, + ) + base_version = _require_positive_integer( + "base_weight_evidence_version", base_weight_evidence_version + ) + if base_version != 1: + raise ValueError("base_weight_evidence_version must remain 1.") + if type(adjustment_bindings) is not tuple: + raise ValueError("adjustment_bindings must be an immutable tuple.") + detached_bindings: list[FinalWeightAdjustmentEvidenceBinding] = [] + for expected_sequence, binding in enumerate(adjustment_bindings, start=1): + if type(binding) is not FinalWeightAdjustmentEvidenceBinding: + raise ValueError( + "adjustment_bindings must contain exact FinalWeightAdjustmentEvidenceBinding values." + ) + detached = FinalWeightAdjustmentEvidenceBinding( + sequence_number=binding.sequence_number, + evidence_kind=binding.evidence_kind, + evidence_receipt_reference=binding.evidence_receipt_reference, + evidence_version=binding.evidence_version, + evidence_receipt_digest=binding.evidence_receipt_digest, + ) + if detached != binding: + raise ValueError("adjustment_bindings must contain canonical receipt locators.") + if detached.sequence_number != expected_sequence: + raise ValueError( + "adjustment binding sequence numbers must be contiguous starting at 1." + ) + detached_bindings.append(detached) + owner_reference = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release > release_instant: + raise ValueError("owner contract must be released no later than component binding.") + cutover = None + if superseded_at is not None: + cutover = _require_aware_datetime("superseded_at", superseded_at) + if cutover <= release_instant: + raise ValueError("superseded_at must be later than released_at.") + fields: tuple[tuple[str, object], ...] = ( + ("adjustment_bindings", tuple(detached_bindings)), + ("analysis_weight_evidence_version", final_version), + ("analysis_weight_receipt_digest", final_receipt_digest), + ("analysis_weight_receipt_reference", final_receipt_reference), + ("base_weight_evidence_receipt_digest", base_receipt_digest), + ("base_weight_evidence_receipt_reference", base_receipt_reference), + ("base_weight_evidence_version", base_version), + ("binding_digest", locator_digest), + ("binding_reference", locator_reference), + ("binding_version", locator_version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_reference), + ("owner_contract_version", owner_version), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + fields, + owner_release, + release_instant, + cutover, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable final-weight and component receipt coordinates.""" + return self[2] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the governing component-binding owner contract was released.""" + return self[3] + + @property + def released_at(self) -> datetime: + """Return when this component locator became released authority.""" + return self[4] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this locator's authority interval.""" + return self[5] + + +class FinalWeightComponentBindingAuthorityView: + """Sealed component locator issued only after purpose authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> FinalWeightComponentBindingAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "FinalWeightComponentBindingAuthorityView is issued only by " + "resolve_final_weight_component_binding_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("FinalWeightComponentBindingAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("FinalWeightComponentBindingAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_final_weight_component_binding_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable component receipt locators and owner chronology.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class FinalWeightComponentBindingAuthorityReadPort(Protocol): + """Owner read contract keyed only by the immutable final-weight receipt identity.""" + + def read_final_weight_component_binding_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + analysis_weight_evidence_version: int, + ) -> FinalWeightComponentBindingAuthorityRecord | None: + """Return the unique released component locator for a final-weight receipt.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + FinalWeightComponentBindingAuthorityReadPort, + "read_final_weight_component_binding_authority", +) + + +def _resolve_final_weight_component_binding_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + analysis_weight_evidence_version: int, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalWeightComponentBindingAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve deterministic typed-component locators for a final weight.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_final_weight_component_binding_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_final_weight_component_binding_authority." + ) + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + final_receipt_reference = _require_reference( + "analysis_weight_receipt_reference", + analysis_weight_receipt_reference, + "analysis_weight_receipt", + ) + final_receipt_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + final_version = _require_positive_integer( + "analysis_weight_evidence_version", analysis_weight_evidence_version + ) + if final_version != 1: + raise ValueError("analysis_weight_evidence_version must remain 1.") + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + analysis_weight_receipt_reference=final_receipt_reference, + analysis_weight_receipt_digest=final_receipt_digest, + analysis_weight_evidence_version=final_version, + ) + if persisted is None: + raise FinalWeightComponentBindingAuthorityNotFound(str(study_id)) + if type(persisted) is not FinalWeightComponentBindingAuthorityRecord: + raise FinalWeightComponentBindingAuthorityIntegrityError( + "owner port returned non-canonical final-weight component binding evidence" + ) + try: + record = FinalWeightComponentBindingAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise FinalWeightComponentBindingAuthorityIntegrityError( + "owner port returned malformed final-weight component binding evidence" + ) from exc + if record != persisted: + raise FinalWeightComponentBindingAuthorityIntegrityError( + "owner port returned non-canonical final-weight component binding evidence" + ) + values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or values["analysis_weight_receipt_reference"] != final_receipt_reference + or values["analysis_weight_receipt_digest"] != final_receipt_digest + or values["analysis_weight_evidence_version"] != final_version + ): + raise FinalWeightComponentBindingAuthorityIntegrityError( + "released final-weight component binding targets another final-weight receipt" + ) + if use_instant < record.released_at: + raise FinalWeightComponentBindingAuthorityIntegrityError( + "final-weight component binding cannot be used before release" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise FinalWeightComponentBindingAuthorityIntegrityError( + "final-weight component binding cannot be used at or after supersession" + ) + values["owner_contract_released_at"] = record.owner_contract_released_at + values["released_at"] = record.released_at + values["superseded_at"] = record.superseded_at + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_final_weight_component_binding_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: FinalWeightComponentBindingAuthorityView) -> None: + """Verify one component-binding view against the private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise FinalWeightComponentBindingAuthorityIntegrityError( + "final-weight component binding view was not issued by " + "resolve_final_weight_component_binding_authority" + ) from exc + if marker is not issuance_marker: + raise FinalWeightComponentBindingAuthorityIntegrityError( + "final-weight component binding view was not issued by " + "resolve_final_weight_component_binding_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + analysis_weight_evidence_version: int, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalWeightComponentBindingAuthorityReadPort, + ) -> FinalWeightComponentBindingAuthorityView: + """Authorize then issue deterministic typed-component locators.""" + tenant_identity, study_identity, fields = ( + _resolve_final_weight_component_binding_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + analysis_weight_receipt_reference=analysis_weight_receipt_reference, + analysis_weight_receipt_digest=analysis_weight_receipt_digest, + analysis_weight_evidence_version=analysis_weight_evidence_version, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(FinalWeightComponentBindingAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_final_weight_component_binding_view_issued, + resolve_final_weight_component_binding_authority, +) = _build_final_weight_component_binding_view_runtime() +del _build_final_weight_component_binding_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_component_evidence_resolution.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_component_evidence_resolution.py new file mode 100644 index 000000000..5db747945 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_component_evidence_resolution.py @@ -0,0 +1,962 @@ +"""Authorize and corroborate exact final-weight component receipt evidence. + +This cross-owner consistency service performs its own purpose-bound authorization +before any owner read. It re-resolves final-weight and component-binding authority, +turns exact component receipt locators into a deterministic resolution contract, +and verifies owner scope, scientific transform semantics, construction chronology, +and governed-use currentness without copying row-level weights or foreign source values. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .final_weight_authority import ( + FinalAnalysisWeightAuthorityRecord, + FinalWeightAdjustmentCoordinate, + _READ_FIELDS as _FINAL_WEIGHT_OWNER_READ_FIELDS, +) +from .final_weight_component_binding_authority import ( + FinalWeightComponentBindingAuthorityRecord, + _EVIDENCE_REFERENCE_NAMESPACE_BY_KIND, + _READ_FIELDS as _BINDING_OWNER_READ_FIELDS, +) +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "final_weight_component_evidence_resolution" +_OPERATION = "read" +_OWNER_SCOPE_FIELDS = frozenset({"tenant_record_id", "validity_study_id"}) +_OWNER_PROVENANCE_READ_FIELDS = ( + _OWNER_SCOPE_FIELDS | _FINAL_WEIGHT_OWNER_READ_FIELDS | _BINDING_OWNER_READ_FIELDS +) +_BASE_READ_FIELDS = frozenset( + { + "tenant_record_id", + "validity_study_id", + "receipt_reference", + "receipt_digest", + "evidence_version", + "method_code", + "method_version", + "output_weight_artifact_digest", + "released_at", + "superseded_at", + } +) +_ADJUSTMENT_READ_FIELDS = frozenset( + { + "tenant_record_id", + "validity_study_id", + "receipt_reference", + "receipt_digest", + "evidence_version", + "method_reference", + "method_version", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "configuration_digest", + "evidence_kind", + "released_at", + "superseded_at", + } +) +class FinalWeightComponentEvidenceNotFound(LookupError): + """Indicate that exact authoritative evidence cannot be deterministically resolved.""" + + +class FinalWeightComponentEvidenceIntegrityError(RuntimeError): + """Indicate that resolved evidence disagrees with final-weight authority.""" + + +class BaseWeightComponentEvidence(tuple): + """Scope-bound base-weight receipt projection needed to reproduce a final weight.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + receipt_reference: str, + receipt_digest: str, + evidence_version: int, + method_code: str, + method_version: int, + output_weight_artifact_digest: str, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> BaseWeightComponentEvidence: + """Validate immutable owner scope, receipt identity, semantics and chronology.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + reference = _require_reference( + "receipt_reference", receipt_reference, "base_weight_evidence_receipt" + ) + digest = _require_digest("receipt_digest", receipt_digest) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1 for base-weight evidence.") + method = _require_code("method_code", method_code) + method_ver = _require_positive_integer("method_version", method_version) + output_digest = _require_digest( + "output_weight_artifact_digest", output_weight_artifact_digest + ) + release = _require_aware_datetime("released_at", released_at) + cutover = None + if superseded_at is not None: + cutover = _require_aware_datetime("superseded_at", superseded_at) + if cutover <= release: + raise ValueError("superseded_at must be later than released_at.") + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + reference, + digest, + version, + method, + method_ver, + output_digest, + release, + cutover, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity from native owner evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity from native owner evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def receipt_reference(self) -> str: + """Return the exact base-weight evidence receipt reference.""" + return self[2] + + @property + def receipt_digest(self) -> str: + """Return the immutable base-weight evidence receipt digest.""" + return self[3] + + @property + def evidence_version(self) -> int: + """Return the governed base-weight evidence version.""" + return self[4] + + @property + def method_code(self) -> str: + """Return the base-weight method code used by the final-weight chain.""" + return self[5] + + @property + def method_version(self) -> int: + """Return the base-weight method version.""" + return self[6] + + @property + def output_weight_artifact_digest(self) -> str: + """Return the resulting base-weight artifact digest.""" + return self[7] + + @property + def released_at(self) -> datetime: + """Return when this component became released authority.""" + return self[8] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this component's authority interval.""" + return self[9] + + +class AdjustmentComponentEvidence(tuple): + """Scope-bound specialized-adjustment projection normalized to final-weight semantics.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + evidence_kind: str, + receipt_reference: str, + receipt_digest: str, + evidence_version: int, + method_reference: str, + method_version: int, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + configuration_digest: str, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> AdjustmentComponentEvidence: + """Validate owner scope, exact receipt identity and transform semantics.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + kind = _require_code("evidence_kind", evidence_kind) + namespace = _EVIDENCE_REFERENCE_NAMESPACE_BY_KIND.get(kind) + if namespace is None: + raise ValueError("evidence_kind must identify a governed specialized receipt.") + reference = _require_reference("receipt_reference", receipt_reference, namespace) + digest = _require_digest("receipt_digest", receipt_digest) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1 for specialized evidence.") + method = _require_reference("method_reference", method_reference, "weight_method") + method_ver = _require_positive_integer("method_version", method_version) + input_digest = _require_digest( + "input_weight_artifact_digest", input_weight_artifact_digest + ) + output_digest = _require_digest( + "output_weight_artifact_digest", output_weight_artifact_digest + ) + if input_digest == output_digest: + raise ValueError( + "output_weight_artifact_digest must identify the transformed weight artifact." + ) + configuration = _require_digest("configuration_digest", configuration_digest) + release = _require_aware_datetime("released_at", released_at) + cutover = None + if superseded_at is not None: + cutover = _require_aware_datetime("superseded_at", superseded_at) + if cutover <= release: + raise ValueError("superseded_at must be later than released_at.") + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + kind, + reference, + digest, + version, + method, + method_ver, + input_digest, + output_digest, + configuration, + release, + cutover, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity from native owner evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity from native owner evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def evidence_kind(self) -> str: + """Return the governed specialized receipt family.""" + return self[2] + + @property + def receipt_reference(self) -> str: + """Return the exact specialized receipt reference.""" + return self[3] + + @property + def receipt_digest(self) -> str: + """Return the immutable specialized receipt digest.""" + return self[4] + + @property + def evidence_version(self) -> int: + """Return the governed specialized evidence version.""" + return self[5] + + @property + def method_reference(self) -> str: + """Return the released weight-method semantic used by the final chain.""" + return self[6] + + @property + def method_version(self) -> int: + """Return the released weight-method version.""" + return self[7] + + @property + def input_weight_artifact_digest(self) -> str: + """Return the transform input artifact digest.""" + return self[8] + + @property + def output_weight_artifact_digest(self) -> str: + """Return the transform output artifact digest.""" + return self[9] + + @property + def configuration_digest(self) -> str: + """Return the immutable transform configuration digest.""" + return self[10] + + @property + def released_at(self) -> datetime: + """Return when this specialized component became released authority.""" + return self[11] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this component's authority interval.""" + return self[12] + + +class FinalWeightComponentEvidenceResolution: + """Immutable proof-bearing component evidence issued only after governed corroboration.""" + + __slots__ = ("__base_weight", "__adjustments", "__issuance_marker") + + def __new__( + cls, + *, + base_weight: BaseWeightComponentEvidence, + adjustments: tuple[AdjustmentComponentEvidence, ...], + ) -> FinalWeightComponentEvidenceResolution: + """Reject public construction so callers cannot mint a corroborated success value.""" + del base_weight, adjustments + raise TypeError( + "FinalWeightComponentEvidenceResolution is issued only by " + "corroborate_final_weight_component_evidence." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Reject mutation; only the private issuer may populate slots with object.__setattr__.""" + del name, value + raise AttributeError("FinalWeightComponentEvidenceResolution is immutable.") + + def __delattr__(self, name: str) -> None: + """Reject deletion from an issued corroboration result.""" + del name + raise AttributeError("FinalWeightComponentEvidenceResolution is immutable.") + + def _require_issued(self) -> None: + """Fail closed when generic allocation produced an unsealed exact runtime object.""" + _require_component_evidence_resolution_issued(self) + + @property + def base_weight(self) -> BaseWeightComponentEvidence: + """Return the exact corroborated base-weight component from a sealed result.""" + self._require_issued() + return object.__getattribute__( + self, + "_FinalWeightComponentEvidenceResolution__base_weight", + ) + + @property + def adjustments(self) -> tuple[AdjustmentComponentEvidence, ...]: + """Return specialized components in final-weight sequence order from a sealed result.""" + self._require_issued() + return object.__getattribute__( + self, + "_FinalWeightComponentEvidenceResolution__adjustments", + ) + + +@runtime_checkable +class FinalWeightComponentEvidenceReadPort(Protocol): + """Owner-resolution contract for final-weight provenance and component evidence.""" + + def read_final_analysis_weight_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + ) -> FinalAnalysisWeightAuthorityRecord | None: + """Resolve the exact released final-weight owner record by immutable receipt identity.""" + ... + + def read_final_weight_component_binding_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + analysis_weight_evidence_version: int, + ) -> FinalWeightComponentBindingAuthorityRecord | None: + """Resolve the exact released binding owner record for the final-weight receipt.""" + ... + + def read_base_weight_component_evidence( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + receipt_reference: str, + receipt_digest: str, + evidence_version: int, + ) -> BaseWeightComponentEvidence | None: + """Resolve one canonical scope-bound base-weight projection by receipt identity.""" + ... + + def read_adjustment_component_evidence( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + evidence_kind: str, + receipt_reference: str, + receipt_digest: str, + evidence_version: int, + ) -> AdjustmentComponentEvidence | None: + """Resolve one canonical scope-bound specialized projection by receipt identity.""" + ... + + +_FINAL_WEIGHT_READ_CAPABILITY = getattr_static( + FinalWeightComponentEvidenceReadPort, "read_final_analysis_weight_authority" +) +_BINDING_READ_CAPABILITY = getattr_static( + FinalWeightComponentEvidenceReadPort, "read_final_weight_component_binding_authority" +) +_BASE_READ_CAPABILITY = getattr_static( + FinalWeightComponentEvidenceReadPort, "read_base_weight_component_evidence" +) +_ADJUSTMENT_READ_CAPABILITY = getattr_static( + FinalWeightComponentEvidenceReadPort, "read_adjustment_component_evidence" +) + + +def _canonical_adjustment_evidence(value: object) -> AdjustmentComponentEvidence: + """Reconstruct specialized projection so exact runtime type cannot hide structure.""" + if type(value) is not AdjustmentComponentEvidence: + raise FinalWeightComponentEvidenceIntegrityError( + "component port returned non-canonical specialized evidence" + ) + try: + canonical = AdjustmentComponentEvidence( + tenant_record_id=value.tenant_record_id, + validity_study_id=value.validity_study_id, + evidence_kind=value.evidence_kind, + receipt_reference=value.receipt_reference, + receipt_digest=value.receipt_digest, + evidence_version=value.evidence_version, + method_reference=value.method_reference, + method_version=value.method_version, + input_weight_artifact_digest=value.input_weight_artifact_digest, + output_weight_artifact_digest=value.output_weight_artifact_digest, + configuration_digest=value.configuration_digest, + released_at=value.released_at, + superseded_at=value.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise FinalWeightComponentEvidenceIntegrityError( + "component port returned malformed specialized evidence" + ) from exc + if canonical != value: + raise FinalWeightComponentEvidenceIntegrityError( + "component port returned non-canonical specialized evidence" + ) + return canonical + + +def _canonical_base_evidence(value: object) -> BaseWeightComponentEvidence: + """Reconstruct base projection so exact runtime type cannot hide structure.""" + if type(value) is not BaseWeightComponentEvidence: + raise FinalWeightComponentEvidenceIntegrityError( + "component port returned non-canonical base-weight evidence" + ) + try: + canonical = BaseWeightComponentEvidence( + tenant_record_id=value.tenant_record_id, + validity_study_id=value.validity_study_id, + receipt_reference=value.receipt_reference, + receipt_digest=value.receipt_digest, + evidence_version=value.evidence_version, + method_code=value.method_code, + method_version=value.method_version, + output_weight_artifact_digest=value.output_weight_artifact_digest, + released_at=value.released_at, + superseded_at=value.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise FinalWeightComponentEvidenceIntegrityError( + "component port returned malformed base-weight evidence" + ) from exc + if canonical != value: + raise FinalWeightComponentEvidenceIntegrityError( + "component port returned non-canonical base-weight evidence" + ) + return canonical + + +def _canonical_final_weight(value: object) -> FinalAnalysisWeightAuthorityRecord: + """Reconstruct final-weight authority before any cross-owner comparison.""" + if type(value) is not FinalAnalysisWeightAuthorityRecord: + raise FinalWeightComponentEvidenceIntegrityError( + "final_weight must be exact canonical final-weight authority evidence" + ) + try: + canonical = FinalAnalysisWeightAuthorityRecord( + tenant_record_id=value.tenant_record_id, + validity_study_id=value.validity_study_id, + owner_contract_released_at=value.owner_contract_released_at, + released_at=value.released_at, + superseded_at=value.superseded_at, + **dict(value.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise FinalWeightComponentEvidenceIntegrityError( + "final_weight is malformed canonical authority evidence" + ) from exc + if canonical != value: + raise FinalWeightComponentEvidenceIntegrityError( + "final_weight contains non-canonical hidden or malformed structure" + ) + return canonical + + +def _canonical_binding(value: object) -> FinalWeightComponentBindingAuthorityRecord: + """Reconstruct component binding before any locator is trusted.""" + if type(value) is not FinalWeightComponentBindingAuthorityRecord: + raise FinalWeightComponentEvidenceIntegrityError( + "binding must be exact canonical final-weight component binding evidence" + ) + try: + canonical = FinalWeightComponentBindingAuthorityRecord( + tenant_record_id=value.tenant_record_id, + validity_study_id=value.validity_study_id, + owner_contract_released_at=value.owner_contract_released_at, + released_at=value.released_at, + superseded_at=value.superseded_at, + **dict(value.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise FinalWeightComponentEvidenceIntegrityError( + "binding is malformed canonical authority evidence" + ) from exc + if canonical != value: + raise FinalWeightComponentEvidenceIntegrityError( + "binding contains non-canonical hidden or malformed structure" + ) + return canonical + + +def _require_component_scope( + *, + component_tenant_record_id: UUID, + component_validity_study_id: UUID, + tenant_record_id: UUID, + validity_study_id: UUID, +) -> None: + """Require normalized component evidence to prove the same owner scope as the final weight.""" + if ( + _store_operational_uuid("component tenant_record_id", component_tenant_record_id) + != _store_operational_uuid("final tenant_record_id", tenant_record_id) + or _store_operational_uuid( + "component validity_study_id", component_validity_study_id + ) + != _store_operational_uuid("final validity_study_id", validity_study_id) + ): + raise FinalWeightComponentEvidenceIntegrityError( + "component evidence belongs to another tenant or validity study" + ) + + +def _require_component_valid_at_construction( + *, released_at: datetime, superseded_at: datetime | None, constructed_at: datetime +) -> None: + """Require component evidence to be released and current when the final weight was built.""" + if released_at > constructed_at: + raise FinalWeightComponentEvidenceIntegrityError( + "component evidence was not released by final-weight construction" + ) + if superseded_at is not None and constructed_at >= superseded_at: + raise FinalWeightComponentEvidenceIntegrityError( + "component evidence was superseded before final-weight construction" + ) + + +def _require_component_current_at_use( + *, superseded_at: datetime | None, used_at: datetime +) -> None: + """Require component authority to remain current at the governed scientific-use instant.""" + if superseded_at is not None and used_at >= superseded_at: + raise FinalWeightComponentEvidenceIntegrityError( + "component evidence is not current at the governed use instant" + ) + + +def _corroborate_final_weight_component_evidence_state( + *, + principal: ValidationPrincipal, + final_weight: FinalAnalysisWeightAuthorityRecord, + binding: FinalWeightComponentBindingAuthorityRecord, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalWeightComponentEvidenceReadPort, +) -> tuple[BaseWeightComponentEvidence, tuple[AdjustmentComponentEvidence, ...]]: + """Authorize and corroborate canonical component evidence into inert state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + final_authority_capability = getattr_static( + type(read_port), "read_final_analysis_weight_authority", None + ) + binding_authority_capability = getattr_static( + type(read_port), "read_final_weight_component_binding_authority", None + ) + base_capability = getattr_static(type(read_port), "read_base_weight_component_evidence", None) + adjustment_capability = getattr_static( + type(read_port), "read_adjustment_component_evidence", None + ) + if ( + type(final_authority_capability) is not FunctionType + or final_authority_capability is _FINAL_WEIGHT_READ_CAPABILITY + ): + raise TypeError("read_port must expose read_final_analysis_weight_authority.") + if ( + type(binding_authority_capability) is not FunctionType + or binding_authority_capability is _BINDING_READ_CAPABILITY + ): + raise TypeError("read_port must expose read_final_weight_component_binding_authority.") + if type(base_capability) is not FunctionType or base_capability is _BASE_READ_CAPABILITY: + raise TypeError("read_port must expose read_base_weight_component_evidence.") + if ( + type(adjustment_capability) is not FunctionType + or adjustment_capability is _ADJUSTMENT_READ_CAPABILITY + ): + raise TypeError("read_port must expose read_adjustment_component_evidence.") + + final_record = _canonical_final_weight(final_weight) + binding_record = _canonical_binding(binding) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + final_values = dict(final_record.fields) + binding_values = dict(binding_record.fields) + requested_fields = _OWNER_PROVENANCE_READ_FIELDS | _BASE_READ_FIELDS + if any( + adjustment.evidence_kind in _EVIDENCE_REFERENCE_NAMESPACE_BY_KIND + for adjustment in final_values["adjustments"] + ): + requested_fields = requested_fields | _ADJUSTMENT_READ_FIELDS + + if ( + _store_operational_uuid("final tenant_record_id", final_record.tenant_record_id) + != _store_operational_uuid("binding tenant_record_id", binding_record.tenant_record_id) + or _store_operational_uuid("final validity_study_id", final_record.validity_study_id) + != _store_operational_uuid("binding validity_study_id", binding_record.validity_study_id) + or final_values["analysis_weight_receipt_reference"] + != binding_values["analysis_weight_receipt_reference"] + or final_values["analysis_weight_receipt_digest"] + != binding_values["analysis_weight_receipt_digest"] + or final_values["evidence_version"] + != binding_values["analysis_weight_evidence_version"] + ): + raise FinalWeightComponentEvidenceIntegrityError( + "component binding targets a different final-weight receipt" + ) + if binding_record.released_at < final_record.released_at: + raise FinalWeightComponentEvidenceIntegrityError( + "component binding cannot be released before final-weight authority" + ) + if use_instant < final_record.released_at or use_instant < binding_record.released_at: + raise FinalWeightComponentEvidenceIntegrityError( + "final-weight evidence and component binding must be released before use" + ) + if final_record.superseded_at is not None and use_instant >= final_record.superseded_at: + raise FinalWeightComponentEvidenceIntegrityError( + "final-weight evidence is not current at the governed use instant" + ) + if binding_record.superseded_at is not None and use_instant >= binding_record.superseded_at: + raise FinalWeightComponentEvidenceIntegrityError( + "component binding is not current at the governed use instant" + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", + _store_operational_uuid("tenant_record_id", final_record.tenant_record_id), + ) + study_id = _restore_operational_uuid( + "validity_study_id", + _store_operational_uuid("validity_study_id", final_record.validity_study_id), + ) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + final_receipt_reference = str(final_values["analysis_weight_receipt_reference"]) + final_receipt_tail = final_receipt_reference.partition(":")[2] + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{final_receipt_tail}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=requested_fields, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + owner_final_value = final_authority_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + analysis_weight_receipt_reference=final_values["analysis_weight_receipt_reference"], + analysis_weight_receipt_digest=final_values["analysis_weight_receipt_digest"], + evidence_version=final_values["evidence_version"], + ) + if owner_final_value is None: + raise FinalWeightComponentEvidenceNotFound( + f"final-weight:{final_values['analysis_weight_receipt_reference']}" + ) + owner_final = _canonical_final_weight(owner_final_value) + if owner_final != final_record: + raise FinalWeightComponentEvidenceIntegrityError( + "final-weight owner authority disagrees with supplied final-weight evidence" + ) + + owner_binding_value = binding_authority_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + analysis_weight_receipt_reference=final_values["analysis_weight_receipt_reference"], + analysis_weight_receipt_digest=final_values["analysis_weight_receipt_digest"], + analysis_weight_evidence_version=final_values["evidence_version"], + ) + if owner_binding_value is None: + raise FinalWeightComponentEvidenceNotFound( + f"binding:{final_values['analysis_weight_receipt_reference']}" + ) + owner_binding = _canonical_binding(owner_binding_value) + if owner_binding != binding_record: + raise FinalWeightComponentEvidenceIntegrityError( + "binding owner authority disagrees with supplied component binding evidence" + ) + + base_value = base_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + receipt_reference=binding_values["base_weight_evidence_receipt_reference"], + receipt_digest=binding_values["base_weight_evidence_receipt_digest"], + evidence_version=binding_values["base_weight_evidence_version"], + ) + if base_value is None: + raise FinalWeightComponentEvidenceNotFound( + str(binding_values["base_weight_evidence_receipt_reference"]) + ) + base = _canonical_base_evidence(base_value) + _require_component_scope( + component_tenant_record_id=base.tenant_record_id, + component_validity_study_id=base.validity_study_id, + tenant_record_id=tenant_id, + validity_study_id=study_id, + ) + if ( + base.receipt_reference != binding_values["base_weight_evidence_receipt_reference"] + or base.receipt_digest != binding_values["base_weight_evidence_receipt_digest"] + or base.evidence_version != binding_values["base_weight_evidence_version"] + or base.receipt_digest != final_values["base_weight_evidence_digest"] + or base.method_code != final_values["base_weight_method_code"] + or base.method_version != final_values["base_weight_method_version"] + or base.output_weight_artifact_digest != final_values["base_weight_artifact_digest"] + ): + raise FinalWeightComponentEvidenceIntegrityError( + "base-weight component semantics disagree with the final-weight receipt" + ) + constructed_at = final_values["constructed_at"] + _require_component_valid_at_construction( + released_at=base.released_at, + superseded_at=base.superseded_at, + constructed_at=constructed_at, + ) + _require_component_current_at_use( + superseded_at=base.superseded_at, + used_at=use_instant, + ) + + adjustments = final_values["adjustments"] + if type(adjustments) is not tuple: + raise FinalWeightComponentEvidenceIntegrityError( + "final-weight adjustments are not canonical immutable coordinates" + ) + adjustment_bindings = binding_values["adjustment_bindings"] + if type(adjustment_bindings) is not tuple: + raise FinalWeightComponentEvidenceIntegrityError( + "component adjustment bindings are not canonical immutable coordinates" + ) + binding_by_sequence = {item.sequence_number: item for item in adjustment_bindings} + adjustment_sequences = {item.sequence_number for item in adjustments} + if set(binding_by_sequence) != adjustment_sequences: + raise FinalWeightComponentEvidenceIntegrityError( + "component binding must cover every governed specialized final-weight adjustment" + ) + + resolved_adjustments: list[AdjustmentComponentEvidence] = [] + for adjustment in adjustments: + if type(adjustment) is not FinalWeightAdjustmentCoordinate: + raise FinalWeightComponentEvidenceIntegrityError( + "final-weight adjustment coordinates must remain canonical" + ) + locator = binding_by_sequence[adjustment.sequence_number] + if ( + locator.evidence_kind != adjustment.evidence_kind + or locator.evidence_receipt_digest != adjustment.evidence_receipt_digest + ): + raise FinalWeightComponentEvidenceIntegrityError( + "component binding digest or evidence kind disagrees with final-weight adjustment" + ) + component_value = adjustment_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + evidence_kind=locator.evidence_kind, + receipt_reference=locator.evidence_receipt_reference, + receipt_digest=locator.evidence_receipt_digest, + evidence_version=locator.evidence_version, + ) + if component_value is None: + raise FinalWeightComponentEvidenceNotFound(locator.evidence_receipt_reference) + component = _canonical_adjustment_evidence(component_value) + _require_component_scope( + component_tenant_record_id=component.tenant_record_id, + component_validity_study_id=component.validity_study_id, + tenant_record_id=tenant_id, + validity_study_id=study_id, + ) + if ( + component.evidence_kind != locator.evidence_kind + or component.receipt_reference != locator.evidence_receipt_reference + or component.receipt_digest != locator.evidence_receipt_digest + or component.evidence_version != locator.evidence_version + ): + raise FinalWeightComponentEvidenceIntegrityError( + "resolved component identity disagrees with the exact binding locator" + ) + if ( + component.method_reference != adjustment.method_reference + or component.method_version != adjustment.method_version + or component.input_weight_artifact_digest != adjustment.input_weight_artifact_digest + or component.output_weight_artifact_digest != adjustment.output_weight_artifact_digest + or component.configuration_digest != adjustment.configuration_digest + ): + raise FinalWeightComponentEvidenceIntegrityError( + "resolved adjustment semantics disagree with the final-weight adjustment" + ) + _require_component_valid_at_construction( + released_at=component.released_at, + superseded_at=component.superseded_at, + constructed_at=constructed_at, + ) + _require_component_current_at_use( + superseded_at=component.superseded_at, + used_at=use_instant, + ) + resolved_adjustments.append(component) + + return base, tuple(resolved_adjustments) + + +def _build_component_evidence_resolution_runtime(): + """Create closure-private sealing state and the authorized public corroborator.""" + issuance_marker = object() + + def require_issued(resolution: FinalWeightComponentEvidenceResolution) -> None: + """Verify one proof result against the closure-private issuance capability.""" + try: + marker = object.__getattribute__( + resolution, + "_FinalWeightComponentEvidenceResolution__issuance_marker", + ) + except AttributeError as exc: + raise FinalWeightComponentEvidenceIntegrityError( + "component evidence resolution was not issued by canonical corroboration" + ) from exc + if marker is not issuance_marker: + raise FinalWeightComponentEvidenceIntegrityError( + "component evidence resolution was not issued by canonical corroboration" + ) + + def corroborate( + *, + principal: ValidationPrincipal, + final_weight: FinalAnalysisWeightAuthorityRecord, + binding: FinalWeightComponentBindingAuthorityRecord, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalWeightComponentEvidenceReadPort, + ) -> FinalWeightComponentEvidenceResolution: + """Authorize, owner-resolve authority, and issue sealed component evidence.""" + base_weight, adjustments = _corroborate_final_weight_component_evidence_state( + principal=principal, + final_weight=final_weight, + binding=binding, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + resolution = object.__new__(FinalWeightComponentEvidenceResolution) + object.__setattr__( + resolution, + "_FinalWeightComponentEvidenceResolution__base_weight", + base_weight, + ) + object.__setattr__( + resolution, + "_FinalWeightComponentEvidenceResolution__adjustments", + adjustments, + ) + object.__setattr__( + resolution, + "_FinalWeightComponentEvidenceResolution__issuance_marker", + issuance_marker, + ) + return resolution + + return require_issued, corroborate + + +( + _require_component_evidence_resolution_issued, + corroborate_final_weight_component_evidence, +) = _build_component_evidence_resolution_runtime() +del _build_component_evidence_resolution_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_supersession_authority.py new file mode 100644 index 000000000..aa61ec801 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/final_weight_supersession_authority.py @@ -0,0 +1,582 @@ +"""Corroborate append-only final analysis-weight correction authority. + +The complete final-weight projection proves what one point-weight construction +contains. This boundary proves *when* that released receipt remained authoritative +and which released successor ended its half-open authority interval. It keeps +row-level weights and case identities with their scientific owners. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "final_weight_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_analysis_weight_receipt_reference", + "successor_correction_sequence", + "successor_analysis_weight_receipt_digest", + "successor_released_at", + } +) +_VIEW_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class FinalWeightSupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the weight receipt.""" + + +class FinalWeightSupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released correction evidence cannot authorize scientific use.""" + + +class FinalWeightSupersessionAuthorityRecord(tuple): + """Immutable owner projection for one final-weight receipt authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_analysis_weight_receipt_reference: str | None = None, + successor_correction_sequence: int | None = None, + successor_analysis_weight_receipt_digest: str | None = None, + successor_released_at: datetime | None = None, + ) -> FinalWeightSupersessionAuthorityRecord: + """Validate released predecessor/successor chronology without weight values.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "analysis_weight_receipt_reference", + analysis_weight_receipt_reference, + "analysis_weight_receipt", + ) + receipt_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + correction = _require_positive_integer("correction_sequence", correction_sequence) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + contract_released_at = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if contract_released_at > release_instant: + raise ValueError( + "owner contract must be released no later than final-weight receipt." + ) + + supersession_values = ( + superseded_at, + successor_analysis_weight_receipt_reference, + successor_correction_sequence, + successor_analysis_weight_receipt_digest, + successor_released_at, + ) + if all(value is None for value in supersession_values): + cutover = None + successor_ref = None + successor_correction = None + successor_digest = None + successor_release = None + elif any(value is None for value in supersession_values): + raise ValueError( + "final-weight supersession requires time and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_analysis_weight_receipt_reference", + successor_analysis_weight_receipt_reference, + "analysis_weight_receipt", + ) + successor_correction = _require_positive_integer( + "successor_correction_sequence", successor_correction_sequence + ) + successor_digest = _require_digest( + "successor_analysis_weight_receipt_digest", + successor_analysis_weight_receipt_digest, + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover < release_instant: + raise ValueError("superseded_at cannot precede final-weight receipt release.") + if successor_ref == receipt_ref: + raise ValueError("successor final-weight receipt must have a new reference.") + if successor_correction != correction + 1: + raise ValueError( + "successor correction_sequence must advance exactly by one." + ) + if successor_digest == receipt_digest: + raise ValueError("successor final-weight receipt must identify new evidence.") + if successor_release <= release_instant: + raise ValueError( + "successor final-weight receipt must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor final-weight receipt must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("analysis_weight_receipt_digest", receipt_digest), + ("analysis_weight_receipt_reference", receipt_ref), + ("correction_sequence", correction), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", contract_released_at), + ("owner_contract_version", owner_version), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_analysis_weight_receipt_digest", successor_digest), + ("successor_analysis_weight_receipt_reference", successor_ref), + ("successor_correction_sequence", successor_correction), + ("successor_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-receipt authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this final-weight receipt became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this receipt's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class FinalWeightSupersessionAuthorityView: + """Sealed current-receipt authority issued only after purpose authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> FinalWeightSupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "FinalWeightSupersessionAuthorityView is issued only by " + "resolve_final_weight_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("FinalWeightSupersessionAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("FinalWeightSupersessionAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_final_weight_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return released current-receipt provenance without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class FinalWeightSupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released final-weight correction state.""" + + def read_final_weight_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> FinalWeightSupersessionAuthorityRecord | None: + """Return matching released supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + FinalWeightSupersessionAuthorityReadPort, + "read_final_weight_supersession_authority", +) + + +def _resolve_final_weight_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalWeightSupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize then resolve the receipt's half-open append-only authority interval.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_final_weight_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_final_weight_supersession_authority." + ) + + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + receipt_ref = _require_reference( + "analysis_weight_receipt_reference", + analysis_weight_receipt_reference, + "analysis_weight_receipt", + ) + receipt_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + correction = _require_positive_integer("correction_sequence", correction_sequence) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + analysis_weight_receipt_reference=receipt_ref, + analysis_weight_receipt_digest=receipt_digest, + evidence_version=version, + correction_sequence=correction, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise FinalWeightSupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not FinalWeightSupersessionAuthorityRecord: + raise FinalWeightSupersessionAuthorityIntegrityError( + "owner port returned non-canonical final-weight supersession evidence" + ) + + try: + successor_values = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = FinalWeightSupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_analysis_weight_receipt_reference=( + None + if successor_values is None + else successor_values["successor_analysis_weight_receipt_reference"] + ), + successor_correction_sequence=( + None + if successor_values is None + else successor_values["successor_correction_sequence"] + ), + successor_analysis_weight_receipt_digest=( + None + if successor_values is None + else successor_values["successor_analysis_weight_receipt_digest"] + ), + successor_released_at=( + None if successor_values is None else successor_values["successor_released_at"] + ), + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise FinalWeightSupersessionAuthorityIntegrityError( + "owner port returned structurally invalid final-weight supersession evidence" + ) from exc + if record != persisted: + raise FinalWeightSupersessionAuthorityIntegrityError( + "owner port returned non-canonical final-weight supersession structure" + ) + + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["analysis_weight_receipt_reference"] != receipt_ref + or record_values["analysis_weight_receipt_digest"] != receipt_digest + or record_values["evidence_version"] != version + or record_values["correction_sequence"] != correction + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise FinalWeightSupersessionAuthorityIntegrityError( + "released final-weight supersession authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise FinalWeightSupersessionAuthorityIntegrityError( + "final-weight authority cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise FinalWeightSupersessionAuthorityIntegrityError( + "superseded final-weight receipt is not authoritative at scientific use" + ) + + values = dict(record.fields) + values["released_at"] = record.released_at + fields = tuple((field_name, values[field_name]) for field_name in sorted(_VIEW_FIELDS)) + return tenant_identity, study_identity, fields + + +def _build_final_weight_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: FinalWeightSupersessionAuthorityView) -> None: + """Verify one final-weight supersession view against the private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise FinalWeightSupersessionAuthorityIntegrityError( + "final-weight supersession view was not issued by " + "resolve_final_weight_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise FinalWeightSupersessionAuthorityIntegrityError( + "final-weight supersession view was not issued by " + "resolve_final_weight_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: FinalWeightSupersessionAuthorityReadPort, + ) -> FinalWeightSupersessionAuthorityView: + """Authorize then issue the current final-weight supersession projection.""" + tenant_identity, study_identity, fields = ( + _resolve_final_weight_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + analysis_weight_receipt_reference=analysis_weight_receipt_reference, + analysis_weight_receipt_digest=analysis_weight_receipt_digest, + evidence_version=evidence_version, + correction_sequence=correction_sequence, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(FinalWeightSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_final_weight_supersession_view_issued, + resolve_final_weight_supersession_authority, +) = _build_final_weight_supersession_view_runtime() +del _build_final_weight_supersession_view_runtime + + +__all__ = [ + "FinalWeightSupersessionAuthorityIntegrityError", + "FinalWeightSupersessionAuthorityNotFound", + "FinalWeightSupersessionAuthorityReadPort", + "FinalWeightSupersessionAuthorityRecord", + "FinalWeightSupersessionAuthorityView", + "resolve_final_weight_supersession_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/nonresponse_adjustment_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/nonresponse_adjustment_authority.py new file mode 100644 index 000000000..134e22fd2 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/nonresponse_adjustment_authority.py @@ -0,0 +1,752 @@ +"""Corroborate released typed nonresponse-adjustment evidence through an owner port. + +This application boundary binds the exact disposition-aware adjustment receipt +that produced a point-weight artifact. It preserves treatment and chronology +evidence without copying response values, source attributes, or row-level +weights. Durable PostgreSQL/release resolution remains a persistence-owner task +after this service reaches protected truth. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "nonresponse_adjustment_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "nonresponse_receipt_reference", + "nonresponse_receipt_digest", + "evidence_version", + "response_disposition_receipt_reference", + "response_disposition_receipt_version", + "response_disposition_receipt_digest", + "response_disposition_receipt_released_at", + "adjustment_population_digest", + "method_reference", + "method_version", + "configuration_digest", + "ineligible_treatment_code", + "unknown_treatment_code", + "unavailable_treatment_code", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class NonresponseAdjustmentAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the nonresponse receipt.""" + + +class NonresponseAdjustmentAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence cannot corroborate the requested nonresponse receipt.""" + + +class NonresponseAdjustmentAuthorityRecord(tuple): + """Immutable owner projection for one released typed nonresponse receipt.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + response_disposition_receipt_reference: str, + response_disposition_receipt_version: int, + response_disposition_receipt_digest: str, + response_disposition_receipt_released_at: datetime, + adjustment_population_digest: str, + method_reference: str, + method_version: int, + configuration_digest: str, + ineligible_treatment_code: str, + unknown_treatment_code: str, + unavailable_treatment_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> NonresponseAdjustmentAuthorityRecord: + """Validate and detach the minimum disposition-aware scientific authority.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "nonresponse_receipt_reference", + nonresponse_receipt_reference, + "nonresponse_adjustment_receipt", + ) + receipt_digest = _require_digest( + "nonresponse_receipt_digest", nonresponse_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + disposition_ref = _require_reference( + "response_disposition_receipt_reference", + response_disposition_receipt_reference, + "response_disposition_receipt", + ) + disposition_version = _require_positive_integer( + "response_disposition_receipt_version", + response_disposition_receipt_version, + ) + disposition_digest = _require_digest( + "response_disposition_receipt_digest", + response_disposition_receipt_digest, + ) + disposition_released = _require_aware_datetime( + "response_disposition_receipt_released_at", + response_disposition_receipt_released_at, + ) + population_digest = _require_digest( + "adjustment_population_digest", adjustment_population_digest + ) + method_ref = _require_reference( + "method_reference", method_reference, "weight_method" + ) + method_ver = _require_positive_integer("method_version", method_version) + configuration = _require_digest("configuration_digest", configuration_digest) + ineligible = _require_code("ineligible_treatment_code", ineligible_treatment_code) + unknown = _require_code("unknown_treatment_code", unknown_treatment_code) + unavailable = _require_code( + "unavailable_treatment_code", unavailable_treatment_code + ) + input_digest = _require_digest( + "input_weight_artifact_digest", input_weight_artifact_digest + ) + output_digest = _require_digest( + "output_weight_artifact_digest", output_weight_artifact_digest + ) + if input_digest == output_digest: + raise ValueError( + "output_weight_artifact_digest must identify the adjusted weight artifact." + ) + constructed = _require_aware_datetime("constructed_at", constructed_at) + if disposition_released > constructed: + raise ValueError( + "response_disposition_receipt_released_at cannot be later than constructed_at." + ) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_released = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + supersession_instant = ( + None + if superseded_at is None + else _require_aware_datetime("superseded_at", superseded_at) + ) + if release_instant < constructed: + raise ValueError("released_at cannot precede constructed_at.") + if owner_released > release_instant: + raise ValueError( + "owner_contract_released_at cannot be later than released_at." + ) + if supersession_instant is not None and supersession_instant <= release_instant: + raise ValueError("superseded_at must be later than released_at.") + + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + receipt_ref, + receipt_digest, + version, + disposition_ref, + disposition_version, + disposition_digest, + disposition_released, + population_digest, + method_ref, + method_ver, + configuration, + ineligible, + unknown, + unavailable, + input_digest, + output_digest, + constructed, + owner_ref, + owner_version, + owner_digest, + owner_released, + release_instant, + supersession_instant, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this released evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity for this released evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def nonresponse_receipt_reference(self) -> str: + """Return the typed nonresponse-adjustment receipt reference.""" + return self[2] + + @property + def nonresponse_receipt_digest(self) -> str: + """Return the exact nonresponse-adjustment receipt digest.""" + return self[3] + + @property + def evidence_version(self) -> int: + """Return the receipt evidence version.""" + return self[4] + + @property + def response_disposition_receipt_reference(self) -> str: + """Return the exact response/disposition input receipt reference.""" + return self[5] + + @property + def response_disposition_receipt_version(self) -> int: + """Return the exact response/disposition input receipt version.""" + return self[6] + + @property + def response_disposition_receipt_digest(self) -> str: + """Return the exact response/disposition input receipt digest.""" + return self[7] + + @property + def response_disposition_receipt_released_at(self) -> datetime: + """Return when the disposition input became released evidence.""" + return self[8] + + @property + def adjustment_population_digest(self) -> str: + """Return the adjustment population digest.""" + return self[9] + + @property + def method_reference(self) -> str: + """Return the controlled nonresponse method reference.""" + return self[10] + + @property + def method_version(self) -> int: + """Return the controlled nonresponse method version.""" + return self[11] + + @property + def configuration_digest(self) -> str: + """Return the immutable method configuration digest.""" + return self[12] + + @property + def ineligible_treatment_code(self) -> str: + """Return the explicit treatment for ineligible cases.""" + return self[13] + + @property + def unknown_treatment_code(self) -> str: + """Return the explicit treatment for unknown dispositions.""" + return self[14] + + @property + def unavailable_treatment_code(self) -> str: + """Return the explicit treatment for unavailable dispositions.""" + return self[15] + + @property + def input_weight_artifact_digest(self) -> str: + """Return the input weight artifact digest.""" + return self[16] + + @property + def output_weight_artifact_digest(self) -> str: + """Return the nonresponse-adjusted output weight artifact digest.""" + return self[17] + + @property + def constructed_at(self) -> datetime: + """Return when the typed nonresponse receipt was constructed.""" + return self[18] + + @property + def owner_contract_reference(self) -> str: + """Return the released owner-contract reference.""" + return self[19] + + @property + def owner_contract_version(self) -> int: + """Return the released owner-contract version.""" + return self[20] + + @property + def owner_contract_digest(self) -> str: + """Return the released owner-contract digest.""" + return self[21] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the governing owner contract became released authority.""" + return self[22] + + @property + def released_at(self) -> datetime: + """Return when this typed nonresponse evidence became released authority.""" + return self[23] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive owner-resolved cutover for this receipt.""" + return self[24] + + +class NonresponseAdjustmentAuthorityView: + """Sealed nonresponse evidence issued only after purpose-bound authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> NonresponseAdjustmentAuthorityView: + """Reject direct construction; only the resolver may issue this view.""" + raise TypeError( + "NonresponseAdjustmentAuthorityView is issued only by " + "resolve_nonresponse_adjustment_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("NonresponseAdjustmentAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("NonresponseAdjustmentAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_nonresponse_adjustment_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable nonresponse provenance without response values.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class NonresponseAdjustmentAuthorityReadPort(Protocol): + """Owner read contract for released typed nonresponse-adjustment evidence.""" + + def read_nonresponse_adjustment_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + response_disposition_receipt_reference: str, + response_disposition_receipt_version: int, + response_disposition_receipt_digest: str, + adjustment_population_digest: str, + method_reference: str, + method_version: int, + configuration_digest: str, + ineligible_treatment_code: str, + unknown_treatment_code: str, + unavailable_treatment_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> NonresponseAdjustmentAuthorityRecord | None: + """Return matching released typed nonresponse evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + NonresponseAdjustmentAuthorityReadPort, "read_nonresponse_adjustment_authority" +) + + +def _coordinate_tuple(record: NonresponseAdjustmentAuthorityRecord) -> tuple[object, ...]: + """Return caller-known coordinates, excluding owner-resolved release instants.""" + return record[:8] + record[9:22] + + +def _resolve_nonresponse_adjustment_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + response_disposition_receipt_reference: str, + response_disposition_receipt_version: int, + response_disposition_receipt_digest: str, + adjustment_population_digest: str, + method_reference: str, + method_version: int, + configuration_digest: str, + ineligible_treatment_code: str, + unknown_treatment_code: str, + unavailable_treatment_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: NonresponseAdjustmentAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize then corroborate the exact released nonresponse receipt.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_nonresponse_adjustment_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_nonresponse_adjustment_authority." + ) + + constructed = _require_aware_datetime("constructed_at", constructed_at) + requested = NonresponseAdjustmentAuthorityRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + nonresponse_receipt_reference=nonresponse_receipt_reference, + nonresponse_receipt_digest=nonresponse_receipt_digest, + evidence_version=evidence_version, + response_disposition_receipt_reference=response_disposition_receipt_reference, + response_disposition_receipt_version=response_disposition_receipt_version, + response_disposition_receipt_digest=response_disposition_receipt_digest, + response_disposition_receipt_released_at=constructed, + adjustment_population_digest=adjustment_population_digest, + method_reference=method_reference, + method_version=method_version, + configuration_digest=configuration_digest, + ineligible_treatment_code=ineligible_treatment_code, + unknown_treatment_code=unknown_treatment_code, + unavailable_treatment_code=unavailable_treatment_code, + input_weight_artifact_digest=input_weight_artifact_digest, + output_weight_artifact_digest=output_weight_artifact_digest, + constructed_at=constructed, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + owner_contract_released_at=constructed, + released_at=constructed, + superseded_at=None, + ) + tenant_id = requested.tenant_record_id + study_id = requested.validity_study_id + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=requested.tenant_record_id, + validity_study_id=requested.validity_study_id, + nonresponse_receipt_reference=requested.nonresponse_receipt_reference, + nonresponse_receipt_digest=requested.nonresponse_receipt_digest, + evidence_version=requested.evidence_version, + response_disposition_receipt_reference=requested.response_disposition_receipt_reference, + response_disposition_receipt_version=requested.response_disposition_receipt_version, + response_disposition_receipt_digest=requested.response_disposition_receipt_digest, + adjustment_population_digest=requested.adjustment_population_digest, + method_reference=requested.method_reference, + method_version=requested.method_version, + configuration_digest=requested.configuration_digest, + ineligible_treatment_code=requested.ineligible_treatment_code, + unknown_treatment_code=requested.unknown_treatment_code, + unavailable_treatment_code=requested.unavailable_treatment_code, + input_weight_artifact_digest=requested.input_weight_artifact_digest, + output_weight_artifact_digest=requested.output_weight_artifact_digest, + constructed_at=requested.constructed_at, + owner_contract_reference=requested.owner_contract_reference, + owner_contract_version=requested.owner_contract_version, + owner_contract_digest=requested.owner_contract_digest, + ) + if persisted is None: + raise NonresponseAdjustmentAuthorityNotFound(str(study_id)) + if type(persisted) is not NonresponseAdjustmentAuthorityRecord: + raise NonresponseAdjustmentAuthorityIntegrityError( + "owner port returned non-canonical nonresponse-adjustment authority evidence" + ) + + try: + record = NonresponseAdjustmentAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + nonresponse_receipt_reference=persisted.nonresponse_receipt_reference, + nonresponse_receipt_digest=persisted.nonresponse_receipt_digest, + evidence_version=persisted.evidence_version, + response_disposition_receipt_reference=persisted.response_disposition_receipt_reference, + response_disposition_receipt_version=persisted.response_disposition_receipt_version, + response_disposition_receipt_digest=persisted.response_disposition_receipt_digest, + response_disposition_receipt_released_at=persisted.response_disposition_receipt_released_at, + adjustment_population_digest=persisted.adjustment_population_digest, + method_reference=persisted.method_reference, + method_version=persisted.method_version, + configuration_digest=persisted.configuration_digest, + ineligible_treatment_code=persisted.ineligible_treatment_code, + unknown_treatment_code=persisted.unknown_treatment_code, + unavailable_treatment_code=persisted.unavailable_treatment_code, + input_weight_artifact_digest=persisted.input_weight_artifact_digest, + output_weight_artifact_digest=persisted.output_weight_artifact_digest, + constructed_at=persisted.constructed_at, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise NonresponseAdjustmentAuthorityIntegrityError( + "owner port returned malformed nonresponse-adjustment authority evidence" + ) from exc + if record != persisted: + raise NonresponseAdjustmentAuthorityIntegrityError( + "owner port returned non-canonical nonresponse-adjustment authority evidence" + ) + if _coordinate_tuple(record) != _coordinate_tuple(requested): + raise NonresponseAdjustmentAuthorityIntegrityError( + "released nonresponse-adjustment authority does not match requested coordinates" + ) + if record.released_at > use_instant: + raise NonresponseAdjustmentAuthorityIntegrityError( + "nonresponse-adjustment evidence must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise NonresponseAdjustmentAuthorityIntegrityError( + "nonresponse-adjustment evidence is superseded for this scientific-use instant" + ) + + fields: tuple[tuple[str, object], ...] = ( + ("adjustment_population_digest", record.adjustment_population_digest), + ("configuration_digest", record.configuration_digest), + ("constructed_at", record.constructed_at), + ("evidence_version", record.evidence_version), + ("ineligible_treatment_code", record.ineligible_treatment_code), + ("input_weight_artifact_digest", record.input_weight_artifact_digest), + ("method_reference", record.method_reference), + ("method_version", record.method_version), + ("nonresponse_receipt_digest", record.nonresponse_receipt_digest), + ("nonresponse_receipt_reference", record.nonresponse_receipt_reference), + ("output_weight_artifact_digest", record.output_weight_artifact_digest), + ("owner_contract_digest", record.owner_contract_digest), + ("owner_contract_reference", record.owner_contract_reference), + ("owner_contract_released_at", record.owner_contract_released_at), + ("owner_contract_version", record.owner_contract_version), + ("released_at", record.released_at), + ("response_disposition_receipt_digest", record.response_disposition_receipt_digest), + ("response_disposition_receipt_reference", record.response_disposition_receipt_reference), + ("response_disposition_receipt_released_at", record.response_disposition_receipt_released_at), + ("response_disposition_receipt_version", record.response_disposition_receipt_version), + ("superseded_at", record.superseded_at), + ("unavailable_treatment_code", record.unavailable_treatment_code), + ("unknown_treatment_code", record.unknown_treatment_code), + ) + return ( + _store_operational_uuid("tenant_record_id", tenant_id), + _store_operational_uuid("validity_study_id", study_id), + fields, + ) + + +def _build_nonresponse_adjustment_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: NonresponseAdjustmentAuthorityView) -> None: + """Verify one nonresponse-adjustment view against the private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise NonresponseAdjustmentAuthorityIntegrityError( + "nonresponse adjustment view was not issued by " + "resolve_nonresponse_adjustment_authority" + ) from exc + if marker is not issuance_marker: + raise NonresponseAdjustmentAuthorityIntegrityError( + "nonresponse adjustment view was not issued by " + "resolve_nonresponse_adjustment_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + response_disposition_receipt_reference: str, + response_disposition_receipt_version: int, + response_disposition_receipt_digest: str, + adjustment_population_digest: str, + method_reference: str, + method_version: int, + configuration_digest: str, + ineligible_treatment_code: str, + unknown_treatment_code: str, + unavailable_treatment_code: str, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: NonresponseAdjustmentAuthorityReadPort, + ) -> NonresponseAdjustmentAuthorityView: + """Authorize then issue the exact released nonresponse receipt projection.""" + tenant_identity, study_identity, fields = _resolve_nonresponse_adjustment_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + nonresponse_receipt_reference=nonresponse_receipt_reference, + nonresponse_receipt_digest=nonresponse_receipt_digest, + evidence_version=evidence_version, + response_disposition_receipt_reference=response_disposition_receipt_reference, + response_disposition_receipt_version=response_disposition_receipt_version, + response_disposition_receipt_digest=response_disposition_receipt_digest, + adjustment_population_digest=adjustment_population_digest, + method_reference=method_reference, + method_version=method_version, + configuration_digest=configuration_digest, + ineligible_treatment_code=ineligible_treatment_code, + unknown_treatment_code=unknown_treatment_code, + unavailable_treatment_code=unavailable_treatment_code, + input_weight_artifact_digest=input_weight_artifact_digest, + output_weight_artifact_digest=output_weight_artifact_digest, + constructed_at=constructed_at, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + view = object.__new__(NonresponseAdjustmentAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_nonresponse_adjustment_view_issued, + resolve_nonresponse_adjustment_authority, +) = _build_nonresponse_adjustment_view_runtime() +del _build_nonresponse_adjustment_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/nonresponse_adjustment_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/nonresponse_adjustment_supersession_authority.py new file mode 100644 index 000000000..73e7e8d29 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/nonresponse_adjustment_supersession_authority.py @@ -0,0 +1,567 @@ +"""Corroborate append-only nonresponse-adjustment correction authority. + +The ordinary nonresponse projection proves which disposition-aware adjustment +receipt produced a released point-weight artifact. This boundary proves when +that immutable receipt remained authoritative and which released successor +ended its half-open authority interval. Successor chronology stays owner-resolved +instead of becoming a caller-selected lookup coordinate. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "nonresponse_adjustment_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "nonresponse_receipt_reference", + "nonresponse_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_nonresponse_receipt_reference", + "successor_nonresponse_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class NonresponseAdjustmentSupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the receipt.""" + + +class NonresponseAdjustmentSupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released correction evidence cannot authorize use.""" + + +class NonresponseAdjustmentSupersessionAuthorityRecord(tuple): + """Immutable owner projection for one nonresponse receipt authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_nonresponse_receipt_reference: str | None = None, + successor_nonresponse_receipt_digest: str | None = None, + successor_evidence_version: int | None = None, + successor_released_at: datetime | None = None, + ) -> NonresponseAdjustmentSupersessionAuthorityRecord: + """Validate one released predecessor and its optional atomic successor edge.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "nonresponse_receipt_reference", + nonresponse_receipt_reference, + "nonresponse_adjustment_receipt", + ) + receipt_digest = _require_digest( + "nonresponse_receipt_digest", nonresponse_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release > release_instant: + raise ValueError( + "owner contract must be released no later than nonresponse receipt." + ) + + successor_values = ( + superseded_at, + successor_nonresponse_receipt_reference, + successor_nonresponse_receipt_digest, + successor_evidence_version, + successor_released_at, + ) + if all(value is None for value in successor_values): + cutover = None + successor_ref = None + successor_digest = None + successor_version = None + successor_release = None + elif any(value is None for value in successor_values): + raise ValueError( + "nonresponse supersession requires cutover and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_nonresponse_receipt_reference", + successor_nonresponse_receipt_reference, + "nonresponse_adjustment_receipt", + ) + successor_digest = _require_digest( + "successor_nonresponse_receipt_digest", + successor_nonresponse_receipt_digest, + ) + successor_version = _require_positive_integer( + "successor_evidence_version", successor_evidence_version + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover <= release_instant: + raise ValueError("superseded_at must be later than nonresponse receipt release.") + if successor_ref == receipt_ref: + raise ValueError("successor nonresponse receipt must have a new reference.") + if successor_digest == receipt_digest: + raise ValueError("successor nonresponse receipt must identify new evidence.") + if successor_version != 1: + raise ValueError("successor_evidence_version must remain 1.") + if successor_release <= release_instant: + raise ValueError( + "successor nonresponse receipt must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor nonresponse receipt must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("evidence_version", version), + ("nonresponse_receipt_digest", receipt_digest), + ("nonresponse_receipt_reference", receipt_ref), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", owner_release), + ("owner_contract_version", owner_version), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_evidence_version", successor_version), + ("successor_nonresponse_receipt_digest", successor_digest), + ("successor_nonresponse_receipt_reference", successor_ref), + ("successor_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-receipt authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this nonresponse receipt became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this receipt's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class NonresponseAdjustmentSupersessionAuthorityView: + """Sealed current-receipt authority issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> NonresponseAdjustmentSupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "NonresponseAdjustmentSupersessionAuthorityView is issued only by " + "resolve_nonresponse_adjustment_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError( + "NonresponseAdjustmentSupersessionAuthorityView is immutable." + ) + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError( + "NonresponseAdjustmentSupersessionAuthorityView is immutable." + ) + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_nonresponse_adjustment_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return current receipt authority without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class NonresponseAdjustmentSupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released nonresponse correction state.""" + + def read_nonresponse_adjustment_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> NonresponseAdjustmentSupersessionAuthorityRecord | None: + """Return matching released nonresponse supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + NonresponseAdjustmentSupersessionAuthorityReadPort, + "read_nonresponse_adjustment_supersession_authority", +) + + +def _resolve_nonresponse_adjustment_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: NonresponseAdjustmentSupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve nonresponse supersession into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_nonresponse_adjustment_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_nonresponse_adjustment_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + receipt_ref = _require_reference( + "nonresponse_receipt_reference", + nonresponse_receipt_reference, + "nonresponse_adjustment_receipt", + ) + receipt_digest = _require_digest( + "nonresponse_receipt_digest", nonresponse_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + nonresponse_receipt_reference=receipt_ref, + nonresponse_receipt_digest=receipt_digest, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise NonresponseAdjustmentSupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not NonresponseAdjustmentSupersessionAuthorityRecord: + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "owner port returned non-canonical nonresponse supersession evidence" + ) + + try: + successor_values = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = NonresponseAdjustmentSupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_nonresponse_receipt_reference=( + None + if successor_values is None + else successor_values["successor_nonresponse_receipt_reference"] + ), + successor_nonresponse_receipt_digest=( + None + if successor_values is None + else successor_values["successor_nonresponse_receipt_digest"] + ), + successor_evidence_version=( + None + if successor_values is None + else successor_values["successor_evidence_version"] + ), + successor_released_at=( + None if successor_values is None else successor_values["successor_released_at"] + ), + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "owner port returned malformed nonresponse supersession evidence" + ) from exc + if record != persisted: + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "owner port returned non-canonical nonresponse supersession evidence" + ) + + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["nonresponse_receipt_reference"] != receipt_ref + or record_values["nonresponse_receipt_digest"] != receipt_digest + or record_values["evidence_version"] != version + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "released nonresponse supersession authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "nonresponse receipt must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "nonresponse receipt is superseded for this scientific-use instant" + ) + + view_values = dict(record.fields) + view_values["released_at"] = record.released_at + fields = tuple( + (name, view_values[name]) + for name in ( + "evidence_version", + "nonresponse_receipt_digest", + "nonresponse_receipt_reference", + "owner_contract_digest", + "owner_contract_reference", + "owner_contract_released_at", + "owner_contract_version", + "released_at", + ) + ) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_nonresponse_adjustment_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: NonresponseAdjustmentSupersessionAuthorityView) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "nonresponse supersession view was not issued by " + "resolve_nonresponse_adjustment_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise NonresponseAdjustmentSupersessionAuthorityIntegrityError( + "nonresponse supersession view was not issued by " + "resolve_nonresponse_adjustment_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + nonresponse_receipt_reference: str, + nonresponse_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: NonresponseAdjustmentSupersessionAuthorityReadPort, + ) -> NonresponseAdjustmentSupersessionAuthorityView: + """Authorize then resolve the nonresponse receipt authority interval.""" + tenant_identity, study_identity, fields = ( + _resolve_nonresponse_adjustment_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + nonresponse_receipt_reference=nonresponse_receipt_reference, + nonresponse_receipt_digest=nonresponse_receipt_digest, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(NonresponseAdjustmentSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_nonresponse_adjustment_supersession_view_issued, + resolve_nonresponse_adjustment_supersession_authority, +) = _build_nonresponse_adjustment_supersession_view_runtime() +del _build_nonresponse_adjustment_supersession_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/py.typed b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/registry.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/registry.py new file mode 100644 index 000000000..c2a1985a8 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/registry.py @@ -0,0 +1,524 @@ +"""Purpose-bound application boundary for the workforce-validation study registry. + +This module deliberately stops before PostgreSQL. The protected foundation still +stores validity-study tables in the legacy foundation schema, while +``ARCHITECTURE.md`` assigns persistence ownership to ``workforce_validation``. +The application contract therefore depends on an owner repository port instead +of normalizing direct cross-context SQL into a long-lived service contract. +""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import getattr_static +import re +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID +from zoneinfo import ZoneInfo + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +_MAX_UUID_INT = (1 << 128) - 1 +_CODE_PATTERN = re.compile(r"^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$") +_REFERENCE_PATTERN = re.compile(r"^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$") +_SCOPE_PATTERN = re.compile(r"^orgmetra(?:\.[a-z][a-z0-9_]*){2,}$") +_RESOURCE_KIND = "validity_study_record" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "criterion_blueprint_id", + "study_status_code", + "recorded_from", + "recorded_to", + } +) + + +class ValidityStudyNotFound(LookupError): + """Indicate that an authorized study identity has no visible registry record.""" + + +class ValidityStudyIntegrityError(RuntimeError): + """Indicate that persistence returned a record outside the authorized target.""" + + +def _require_operational_uuid(field_name: str, value: object) -> int: + """Return one inert UUID integer after exact outer and internal-type validation.""" + if type(value) is not UUID: + raise ValueError(f"{field_name} must be an exact operational UUID.") + identity = value.int + if type(identity) is not int or identity <= 0 or identity >= _MAX_UUID_INT: + raise ValueError(f"{field_name} must be an exact operational UUID.") + return identity + + +def _store_operational_uuid(field_name: str, value: object) -> int: + """Reduce one validated UUID to immutable integer storage without retaining its object alias.""" + return _require_operational_uuid(field_name, value) + + +def _restore_operational_uuid(field_name: str, value: object) -> UUID: + """Reconstruct one fresh UUID from immutable internal integer storage.""" + if type(value) is not int or value <= 0 or value >= _MAX_UUID_INT: + raise ValueError(f"{field_name} must be an exact operational UUID.") + return UUID(int=value) + + +def _require_code(field_name: str, value: object) -> str: + """Return one exact lower-snake-case code used in an auditable policy request.""" + if type(value) is not str or _CODE_PATTERN.fullmatch(value) is None: + raise ValueError(f"{field_name} must be an exact lower snake_case code.") + return value + + +def _require_aware_datetime(field_name: str, value: object) -> datetime: + """Detach one durable timestamp to exact UTC without arbitrary timezone callbacks.""" + if type(value) is not datetime: + raise ValueError(f"{field_name} must be an exact datetime.") + provider = value.tzinfo + if type(provider) is not timezone and type(provider) is not ZoneInfo: + raise ValueError(f"{field_name} must use a standard-library timezone provider.") + return value.astimezone(timezone.utc) + + +def _validate_scope_set(values: object) -> frozenset[str]: + """Require immutable explicit Keyverse scopes before constructing an access request.""" + if type(values) is not frozenset or not values: + raise ValueError("granted_scope_codes must be a non-empty exact frozenset.") + if any(type(value) is not str or _SCOPE_PATTERN.fullmatch(value) is None for value in values): + raise ValueError("granted_scope_codes must contain exact Orgmetra scopes.") + return values + + +def _validate_requested_fields(values: object) -> frozenset[str]: + """Require a non-empty immutable subset of the published registry read fields.""" + if type(values) is not frozenset or not values: + raise ValueError("requested_fields must be a non-empty exact frozenset.") + if any(type(value) is not str for value in values) or not values.issubset(_READ_FIELDS): + raise ValueError("requested_fields contains a field outside the validity-study registry contract.") + return values + + +def _detach_policy(policy: PurposeBoundAccessPolicy) -> PurposeBoundAccessPolicy: + """Copy policy evidence into exact inert values before any authorization comparison. + + The protected Keyverse adapter accepts subclass-compatible scalar inputs for + backward compatibility. This owner boundary is stricter because a caller- + defined ``str``/``UUID`` subtype could otherwise execute Python behavior when + the evaluator compares or hashes policy attributes. Immutable UUID integer + storage also prevents a retained policy UUID alias from switching the tenant + after this boundary has accepted it. + """ + tenant_record_id = policy.tenant_record_id + policy_version_code = policy.policy_version_code + resource_kind = policy.resource_kind + purpose_code = policy.purpose_code + operation_code = policy.operation_code + required_scope_code = policy.required_scope_code + permitted_fields = policy.permitted_fields + + tenant_identity = _store_operational_uuid("policy tenant_record_id", tenant_record_id) + for field_name, value in ( + ("policy_version_code", policy_version_code), + ("resource_kind", resource_kind), + ("purpose_code", purpose_code), + ("operation_code", operation_code), + ("required_scope_code", required_scope_code), + ): + if type(value) is not str: + raise ValueError(f"policy {field_name} must be an exact string.") + if type(permitted_fields) is not frozenset or any( + type(value) is not str for value in permitted_fields + ): + raise ValueError("policy permitted_fields must contain exact strings in an exact frozenset.") + + return PurposeBoundAccessPolicy( + tenant_record_id=_restore_operational_uuid("policy tenant_record_id", tenant_identity), + policy_version_code=policy_version_code, + resource_kind=resource_kind, + purpose_code=purpose_code, + operation_code=operation_code, + required_scope_code=required_scope_code, + permitted_fields=permitted_fields, + ) + + +class ValidationPrincipal(tuple): + """Structurally immutable authenticated Keyverse attributes for validation reads. + + The bearer credential itself never enters this value. Tuple-backed storage + keeps only immutable UUID integer evidence plus immutable actor/scope values, + so retained UUID references cannot rewrite tenant identity after validation. + """ + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + actor_reference: str, + granted_scope_codes: frozenset[str], + ) -> ValidationPrincipal: + """Validate exact identity evidence before creating the immutable principal.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + if type(actor_reference) is not str or _REFERENCE_PATTERN.fullmatch(actor_reference) is None: + raise ValueError("actor_reference must be an exact namespaced opaque reference.") + scope_codes = _validate_scope_set(granted_scope_codes) + return tuple.__new__(cls, (tenant_identity, actor_reference, scope_codes)) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authenticated tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def actor_reference(self) -> str: + """Return the opaque authenticated actor reference.""" + return self[1] + + @property + def granted_scope_codes(self) -> frozenset[str]: + """Return the immutable authenticated scope set.""" + return self[2] + + +class ValidityStudyRecord(tuple): + """Structurally immutable owner projection of one recorded validity-study header. + + The tuple-backed representation stores UUIDs as immutable integers, preventing + a repository adapter that retains accepted UUID objects from rewriting durable + study identity through ``object.__setattr__`` after construction. Only fields + already represented by the protected foundation schema are carried here. + Predictor, sample, decision-policy and analysis-protocol versions remain a + later scientific-model increment owned by Issue #234. + """ + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + criterion_blueprint_id: UUID, + study_status_code: str, + recorded_from: datetime, + recorded_to: datetime | None, + ) -> ValidityStudyRecord: + """Validate and detach durable scalars before creating the immutable tuple.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + criterion_identity = _store_operational_uuid( + "criterion_blueprint_id", criterion_blueprint_id + ) + status_code = _require_code("study_status_code", study_status_code) + recorded_start = _require_aware_datetime("recorded_from", recorded_from) + recorded_end = ( + None + if recorded_to is None + else _require_aware_datetime("recorded_to", recorded_to) + ) + if recorded_end is not None and recorded_end <= recorded_start: + raise ValueError("recorded_to must be later than recorded_from.") + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + criterion_identity, + status_code, + recorded_start, + recorded_end, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this validity study.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh stable validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def criterion_blueprint_id(self) -> UUID: + """Return a fresh criterion-blueprint identity linked to the study header.""" + return _restore_operational_uuid("criterion_blueprint_id", self[2]) + + @property + def study_status_code(self) -> str: + """Return the stored study status code without inferring lifecycle governance.""" + return self[3] + + @property + def recorded_from(self) -> datetime: + """Return the exact UTC instant when this version became recorded truth.""" + return self[4] + + @property + def recorded_to(self) -> datetime | None: + """Return the exact UTC close instant when present.""" + return self[5] + + +def _store_view_fields(fields: tuple[tuple[str, object], ...]) -> tuple[tuple[str, object], ...]: + """Store UUID-valued projection fields without retaining mutable UUID object aliases.""" + return tuple( + ( + field_name, + _store_operational_uuid(field_name, value) + if field_name == "criterion_blueprint_id" + else value, + ) + for field_name, value in fields + ) + + +def _restore_view_fields(fields: tuple[tuple[str, object], ...]) -> tuple[tuple[str, object], ...]: + """Return a public projection with fresh UUID objects for UUID-valued fields.""" + return tuple( + ( + field_name, + _restore_operational_uuid(field_name, value) + if field_name == "criterion_blueprint_id" + else value, + ) + for field_name, value in fields + ) + + +class ValidityStudyView: + """Sealed field-minimized data view returned only after authorization. + + The public constructor is deliberately non-issuing. A raw object allocation + remains unusable because every public property verifies closure-private + read-path issuance before exposing detached projection state. The runtime type + is still data, not a reusable authorization credential; consequential actions + must re-authorize and re-resolve owner truth. + """ + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> ValidityStudyView: + """Reject public construction so only the authorized read path issues views.""" + raise TypeError("ValidityStudyView is issued only by read_validity_study.") + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("ValidityStudyView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("ValidityStudyView is immutable.") + + def _require_issued(self) -> None: + """Reject raw exact-runtime allocations that were not sealed by the read path.""" + _require_validity_study_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity authorized for this view.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity authorized for this view.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return ordered field-minimized evidence with fresh UUID-valued projections.""" + self._require_issued() + return _restore_view_fields(object.__getattribute__(self, "_fields")) + + +@runtime_checkable +class ValidityStudyReadPort(Protocol): + """Owner repository contract for one tenant-local validity-study header.""" + + def read_validity_study( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + ) -> ValidityStudyRecord | None: + """Return one visible owner record or ``None`` without crossing service tables.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static(ValidityStudyReadPort, "read_validity_study") + + +def _read_validity_study_authorized_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + purpose_code: str, + requested_fields: frozenset[str], + policy: PurposeBoundAccessPolicy, + read_port: ValidityStudyReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve one study into inert state without issuing a public view. + + Authorization is completed before persistence. The exact ordinary repository + method is captured inertly before authorization and that same function is + invoked after authorization, so dynamic instance lookup cannot switch the + validated capability. Immutable integer snapshots preserve the authorized + target across the executable repository call. The persistence result is + reconstructed into an exact immutable value and must match those snapshots + before projected state is returned to the closure-private issuer. + """ + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static(type(read_port), "read_validity_study", None) + if type(read_capability) is not FunctionType or read_capability is _PROTOCOL_READ_CAPABILITY: + raise TypeError("read_port must expose a statically callable read_validity_study.") + + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + purpose = _require_code("purpose_code", purpose_code) + fields = _validate_requested_fields(requested_fields) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=fields, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=_restore_operational_uuid("tenant_record_id", tenant_identity), + validity_study_id=_restore_operational_uuid("validity_study_id", study_identity), + ) + if persisted is None: + raise ValidityStudyNotFound(str(study_id)) + if type(persisted) is not ValidityStudyRecord: + raise ValidityStudyIntegrityError("repository returned a non-canonical validity-study record") + + try: + record = ValidityStudyRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + criterion_blueprint_id=persisted.criterion_blueprint_id, + study_status_code=persisted.study_status_code, + recorded_from=persisted.recorded_from, + recorded_to=persisted.recorded_to, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise ValidityStudyIntegrityError( + "repository returned a structurally invalid validity-study record" + ) from exc + if record != persisted: + raise ValidityStudyIntegrityError( + "repository returned a non-canonical validity-study record" + ) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != tenant_identity + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != study_identity + ): + raise ValidityStudyIntegrityError("repository returned a validity-study record for another target") + + values = { + "criterion_blueprint_id": record.criterion_blueprint_id, + "study_status_code": record.study_status_code, + "recorded_from": record.recorded_from, + "recorded_to": record.recorded_to, + } + projected_fields = tuple((field_name, values[field_name]) for field_name in sorted(fields)) + return tenant_identity, study_identity, _store_view_fields(projected_fields) + + +def _build_validity_study_view_runtime(): + """Create closure-private sealing state and the only supported public issuer.""" + issuance_marker = object() + + def require_issued(view: ValidityStudyView) -> None: + """Verify one view against the closure-private issuance capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise ValidityStudyIntegrityError( + "validity-study view was not issued by read_validity_study" + ) from exc + if marker is not issuance_marker: + raise ValidityStudyIntegrityError( + "validity-study view was not issued by read_validity_study" + ) + + def issue_after_authorized_read( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + purpose_code: str, + requested_fields: frozenset[str], + policy: PurposeBoundAccessPolicy, + read_port: ValidityStudyReadPort, + ) -> ValidityStudyView: + """Issue one sealed view only after the canonical authorized owner read succeeds.""" + tenant_identity, study_identity, projected_fields = ( + _read_validity_study_authorized_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + purpose_code=purpose_code, + requested_fields=requested_fields, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(ValidityStudyView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", projected_fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, issue_after_authorized_read + + +_require_validity_study_view_issued, read_validity_study = _build_validity_study_view_runtime() +del _build_validity_study_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_authority.py new file mode 100644 index 000000000..463cedf20 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_authority.py @@ -0,0 +1,617 @@ +"""Corroborate released validation-result evidence through an owner port. + +This application boundary binds one immutable validation result to the exact +point-weight/variance compatibility evidence it claims to use. It keeps the +scientific leaf non-authorizing: only ``verification_pending`` or +``not_verifiable`` may cross this owner boundary. Release and supersession +instants are owner evidence, never caller assertions. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "validation_result_authority" +_OPERATION = "read" +_VERIFICATION_STATUSES = frozenset({"verification_pending", "not_verifiable"}) +_READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "compatibility_receipt_reference", + "compatibility_receipt_digest", + "analysis_weight_receipt_digest", + "variance_design_receipt_digest", + "verification_status", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class ValidationResultAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the result tuple.""" + + +class ValidationResultAuthorityIntegrityError(RuntimeError): + """Indicate that released owner evidence cannot support the requested result.""" + + +def _require_verification_status(value: object) -> str: + """Require one explicit non-authorizing result verification state.""" + if type(value) is not str or value not in _VERIFICATION_STATUSES: + raise ValueError( + "verification_status must be verification_pending or not_verifiable." + ) + return value + + +class ValidationResultAuthorityRecord(tuple): + """Immutable owner projection binding result, weight, variance, and chronology.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + compatibility_receipt_reference: str, + compatibility_receipt_digest: str, + analysis_weight_receipt_digest: str, + variance_design_receipt_digest: str, + verification_status: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> ValidationResultAuthorityRecord: + """Validate the released binding and its owner-resolved authority interval.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + compatibility_ref = _require_reference( + "compatibility_receipt_reference", + compatibility_receipt_reference, + "weight_variance_compatibility_receipt", + ) + compatibility_digest = _require_digest( + "compatibility_receipt_digest", compatibility_receipt_digest + ) + point_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + variance_digest = _require_digest( + "variance_design_receipt_digest", variance_design_receipt_digest + ) + if len( + { + result_evidence_digest, + compatibility_digest, + point_digest, + variance_digest, + } + ) != 4: + raise ValueError( + "result, compatibility, analysis-weight, and variance evidence must be distinct." + ) + status = _require_verification_status(verification_status) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + contract_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if contract_release > release_instant: + raise ValueError( + "owner contract must be released no later than validation result." + ) + cutover = ( + None + if superseded_at is None + else _require_aware_datetime("superseded_at", superseded_at) + ) + if cutover is not None and cutover <= release_instant: + raise ValueError("superseded_at must be later than validation-result release.") + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + result_ref, + result_evidence_digest, + compatibility_ref, + compatibility_digest, + point_digest, + variance_digest, + status, + owner_ref, + owner_version, + owner_digest, + contract_release, + release_instant, + cutover, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this released evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity for this released evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def result_reference(self) -> str: + """Return the immutable scientific result reference.""" + return self[2] + + @property + def result_digest(self) -> str: + """Return the digest of the exact scientific result bytes.""" + return self[3] + + @property + def compatibility_receipt_reference(self) -> str: + """Return the exact point-weight/variance compatibility receipt reference.""" + return self[4] + + @property + def compatibility_receipt_digest(self) -> str: + """Return the exact compatibility receipt digest.""" + return self[5] + + @property + def analysis_weight_receipt_digest(self) -> str: + """Return the final point-estimation weight receipt digest.""" + return self[6] + + @property + def variance_design_receipt_digest(self) -> str: + """Return the distinct variance-design receipt digest.""" + return self[7] + + @property + def verification_status(self) -> str: + """Return the non-authorizing scientific verification state.""" + return self[8] + + @property + def owner_contract_reference(self) -> str: + """Return the released owner-contract reference.""" + return self[9] + + @property + def owner_contract_version(self) -> int: + """Return the positive released owner-contract version.""" + return self[10] + + @property + def owner_contract_digest(self) -> str: + """Return the immutable released owner-contract digest.""" + return self[11] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the owner contract became released authority.""" + return self[12] + + @property + def released_at(self) -> datetime: + """Return when this result-authority evidence became released.""" + return self[13] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this result binding's authority interval.""" + return self[14] + + +class ValidationResultAuthorityView: + """Sealed released result evidence issued only after purpose-bound authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> ValidationResultAuthorityView: + """Reject direct construction; only the resolver may issue this view.""" + raise TypeError( + "ValidationResultAuthorityView is issued only by " + "resolve_validation_result_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("ValidationResultAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("ValidationResultAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_validation_result_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable corroborating fields without row-level scientific data.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class ValidationResultAuthorityReadPort(Protocol): + """Owner read contract for released result-to-weight/variance evidence.""" + + def read_validation_result_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + compatibility_receipt_reference: str, + compatibility_receipt_digest: str, + analysis_weight_receipt_digest: str, + variance_design_receipt_digest: str, + verification_status: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultAuthorityRecord | None: + """Return matching released evidence or ``None`` through an owner ACL.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + ValidationResultAuthorityReadPort, "read_validation_result_authority" +) + + +def _resolve_validation_result_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + compatibility_receipt_reference: str, + compatibility_receipt_digest: str, + analysis_weight_receipt_digest: str, + variance_design_receipt_digest: str, + verification_status: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize then corroborate one exact released scientific-result binding.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_validation_result_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_validation_result_authority." + ) + + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + compatibility_ref = _require_reference( + "compatibility_receipt_reference", + compatibility_receipt_reference, + "weight_variance_compatibility_receipt", + ) + compatibility_digest = _require_digest( + "compatibility_receipt_digest", compatibility_receipt_digest + ) + point_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + variance_digest = _require_digest( + "variance_design_receipt_digest", variance_design_receipt_digest + ) + if len( + {result_evidence_digest, compatibility_digest, point_digest, variance_digest} + ) != 4: + raise ValueError( + "result, compatibility, analysis-weight, and variance evidence must be distinct." + ) + status = _require_verification_status(verification_status) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=_restore_operational_uuid("tenant_record_id", tenant_identity), + validity_study_id=_restore_operational_uuid("validity_study_id", study_identity), + result_reference=result_ref, + result_digest=result_evidence_digest, + compatibility_receipt_reference=compatibility_ref, + compatibility_receipt_digest=compatibility_digest, + analysis_weight_receipt_digest=point_digest, + variance_design_receipt_digest=variance_digest, + verification_status=status, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise ValidationResultAuthorityNotFound(str(study_id)) + if type(persisted) is not ValidationResultAuthorityRecord: + raise ValidationResultAuthorityIntegrityError( + "owner port returned non-canonical validation-result authority evidence" + ) + + try: + record = ValidationResultAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + result_reference=persisted.result_reference, + result_digest=persisted.result_digest, + compatibility_receipt_reference=persisted.compatibility_receipt_reference, + compatibility_receipt_digest=persisted.compatibility_receipt_digest, + analysis_weight_receipt_digest=persisted.analysis_weight_receipt_digest, + variance_design_receipt_digest=persisted.variance_design_receipt_digest, + verification_status=persisted.verification_status, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise ValidationResultAuthorityIntegrityError( + "owner port returned malformed validation-result authority evidence" + ) from exc + if record != persisted: + raise ValidationResultAuthorityIntegrityError( + "owner port returned non-canonical validation-result authority structure" + ) + + requested_identity = ( + tenant_identity, + study_identity, + result_ref, + result_evidence_digest, + compatibility_ref, + compatibility_digest, + point_digest, + variance_digest, + status, + owner_ref, + owner_version, + owner_digest, + ) + record_identity = ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id), + _store_operational_uuid("record validity_study_id", record.validity_study_id), + record.result_reference, + record.result_digest, + record.compatibility_receipt_reference, + record.compatibility_receipt_digest, + record.analysis_weight_receipt_digest, + record.variance_design_receipt_digest, + record.verification_status, + record.owner_contract_reference, + record.owner_contract_version, + record.owner_contract_digest, + ) + if record_identity != requested_identity: + raise ValidationResultAuthorityIntegrityError( + "owner evidence does not match the requested validation-result binding" + ) + if use_instant < record.released_at: + raise ValidationResultAuthorityIntegrityError( + "validation-result authority cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise ValidationResultAuthorityIntegrityError( + "validation-result authority ended at its owner-resolved supersession instant" + ) + + values = { + "result_reference": record.result_reference, + "result_digest": record.result_digest, + "compatibility_receipt_reference": record.compatibility_receipt_reference, + "compatibility_receipt_digest": record.compatibility_receipt_digest, + "analysis_weight_receipt_digest": record.analysis_weight_receipt_digest, + "variance_design_receipt_digest": record.variance_design_receipt_digest, + "verification_status": record.verification_status, + "owner_contract_reference": record.owner_contract_reference, + "owner_contract_version": record.owner_contract_version, + "owner_contract_digest": record.owner_contract_digest, + "owner_contract_released_at": record.owner_contract_released_at, + "released_at": record.released_at, + "superseded_at": record.superseded_at, + } + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return tenant_identity, study_identity, fields + + +def _build_validation_result_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: ValidationResultAuthorityView) -> None: + """Verify one validation-result view against the private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise ValidationResultAuthorityIntegrityError( + "validation result view was not issued by " + "resolve_validation_result_authority" + ) from exc + if marker is not issuance_marker: + raise ValidationResultAuthorityIntegrityError( + "validation result view was not issued by " + "resolve_validation_result_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + compatibility_receipt_reference: str, + compatibility_receipt_digest: str, + analysis_weight_receipt_digest: str, + variance_design_receipt_digest: str, + verification_status: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultAuthorityReadPort, + ) -> ValidationResultAuthorityView: + """Authorize then issue one exact released scientific-result binding.""" + tenant_identity, study_identity, fields = ( + _resolve_validation_result_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + result_reference=result_reference, + result_digest=result_digest, + compatibility_receipt_reference=compatibility_receipt_reference, + compatibility_receipt_digest=compatibility_receipt_digest, + analysis_weight_receipt_digest=analysis_weight_receipt_digest, + variance_design_receipt_digest=variance_design_receipt_digest, + verification_status=verification_status, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(ValidationResultAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_validation_result_view_issued, + resolve_validation_result_authority, +) = _build_validation_result_view_runtime() +del _build_validation_result_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability.py new file mode 100644 index 000000000..653848342 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability.py @@ -0,0 +1,748 @@ +"""Represent released non-authorizing outcomes when result evidence cannot be verified. + +This application boundary exists for #407 RED 12: absence or failed reproducibility +of required point-weight/compatibility/variance evidence must become explicit +``not_verifiable`` owner evidence rather than an exception that callers can +misinterpret as scientific GREEN. Durable PostgreSQL resolution remains a child +persistence responsibility after the canonical service owner lands. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "validation_result_nonverifiability" +_OPERATION = "read" +_VERIFICATION_STATUS = "not_verifiable" +_FAILED_REFERENCE_KIND_BY_EVIDENCE_KIND = { + "analysis_weight_receipt": "analysis_weight_receipt", + "weight_variance_compatibility_receipt": "weight_variance_compatibility_receipt", + "variance_design_receipt": "variance_design_receipt", +} +_FAILURE_MODES = frozenset({"missing", "non_reproducible"}) +_READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "verification_status", + "failed_evidence_kind", + "failure_mode", + "failed_evidence_reference", + "failed_evidence_digest", + "failed_evidence_released_at", + "verification_attempt_reference", + "verification_attempt_digest", + "verification_attempt_released_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "evaluated_at", + "released_at", + "superseded_at", + } +) + + +class ValidationResultNonVerifiabilityNotFound(LookupError): + """Indicate that no released owner outcome corroborates the requested failure.""" + + +class ValidationResultNonVerifiabilityIntegrityError(RuntimeError): + """Indicate that released owner outcome evidence is inconsistent or malformed.""" + + +def _require_failed_evidence_kind(value: object) -> str: + """Require one supported scientific evidence family.""" + if type(value) is not str or value not in _FAILED_REFERENCE_KIND_BY_EVIDENCE_KIND: + raise ValueError( + "failed_evidence_kind must be analysis_weight_receipt, " + "weight_variance_compatibility_receipt, or variance_design_receipt." + ) + return value + + +def _require_failure_mode(value: object) -> str: + """Require a missing or non-reproducible evidence outcome.""" + if type(value) is not str or value not in _FAILURE_MODES: + raise ValueError("failure_mode must be missing or non_reproducible.") + return value + + +class ValidationResultNonVerifiabilityRecord(tuple): + """Immutable owner projection for one released ``not_verifiable`` outcome.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + failed_evidence_reference: str | None, + failed_evidence_digest: str | None, + verification_attempt_reference: str, + verification_attempt_digest: str, + verification_attempt_released_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + evaluated_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + failed_evidence_released_at: datetime | None = None, + ) -> ValidationResultNonVerifiabilityRecord: + """Validate a reproducible failure explanation and its authority chronology.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + evidence_kind = _require_failed_evidence_kind(failed_evidence_kind) + mode = _require_failure_mode(failure_mode) + + failed_reference: str | None + failed_digest: str | None + failed_release: datetime | None + if mode == "missing": + if ( + failed_evidence_reference is not None + or failed_evidence_digest is not None + or failed_evidence_released_at is not None + ): + raise ValueError( + "failed evidence reference, digest, and release chronology must be absent " + "when evidence is missing." + ) + failed_reference = None + failed_digest = None + failed_release = None + else: + if failed_evidence_reference is None or failed_evidence_digest is None: + raise ValueError( + "failed evidence reference and digest are required for non_reproducible evidence." + ) + if failed_evidence_released_at is None: + raise ValueError( + "failed_evidence_released_at is required for non_reproducible evidence." + ) + failed_reference = _require_reference( + "failed_evidence_reference", + failed_evidence_reference, + _FAILED_REFERENCE_KIND_BY_EVIDENCE_KIND[evidence_kind], + ) + failed_digest = _require_digest( + "failed_evidence_digest", failed_evidence_digest + ) + failed_release = _require_aware_datetime( + "failed_evidence_released_at", failed_evidence_released_at + ) + + attempt_ref = _require_reference( + "verification_attempt_reference", + verification_attempt_reference, + "validation_evidence_verification_attempt", + ) + attempt_digest = _require_digest( + "verification_attempt_digest", verification_attempt_digest + ) + attempt_release = _require_aware_datetime( + "verification_attempt_released_at", verification_attempt_released_at + ) + owner_ref = _require_reference( + "owner_contract_reference", + owner_contract_reference, + "released_owner_contract", + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_released = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + + digests = [result_evidence_digest, attempt_digest, owner_digest] + if failed_digest is not None: + digests.append(failed_digest) + if len(set(digests)) != len(digests): + raise ValueError( + "result, failed-evidence, verification-attempt, and owner-contract " + "digests must be distinct when present." + ) + + evaluation_instant = _require_aware_datetime("evaluated_at", evaluated_at) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_released > evaluation_instant: + raise ValueError( + "owner_contract_released_at cannot be later than evaluated_at." + ) + if failed_release is not None and failed_release > evaluation_instant: + raise ValueError( + "failed_evidence_released_at cannot be later than evaluated_at." + ) + if evaluation_instant > release_instant: + raise ValueError("evaluated_at cannot be later than released_at.") + if attempt_release < evaluation_instant: + raise ValueError( + "verification_attempt_released_at cannot precede evaluated_at." + ) + if attempt_release > release_instant: + raise ValueError( + "verification_attempt_released_at cannot be later than released_at." + ) + cutover = ( + None + if superseded_at is None + else _require_aware_datetime("superseded_at", superseded_at) + ) + if cutover is not None and cutover <= release_instant: + raise ValueError( + "superseded_at must be later than non-verifiability release." + ) + + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + result_ref, + result_evidence_digest, + evidence_kind, + mode, + failed_reference, + failed_digest, + attempt_ref, + attempt_digest, + owner_ref, + owner_version, + owner_digest, + owner_released, + evaluation_instant, + release_instant, + cutover, + failed_release, + attempt_release, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def result_reference(self) -> str: + """Return the immutable result reference that failed verification.""" + return self[2] + + @property + def result_digest(self) -> str: + """Return the exact result digest.""" + return self[3] + + @property + def verification_status(self) -> str: + """Return the only state this evidence can authorize.""" + return _VERIFICATION_STATUS + + @property + def failed_evidence_kind(self) -> str: + """Return which required evidence family failed verification.""" + return self[4] + + @property + def failure_mode(self) -> str: + """Return whether required evidence was missing or non-reproducible.""" + return self[5] + + @property + def failed_evidence_reference(self) -> str | None: + """Return the failed evidence reference when a non-reproducible artifact exists.""" + return self[6] + + @property + def failed_evidence_digest(self) -> str | None: + """Return the failed evidence digest when a non-reproducible artifact exists.""" + return self[7] + + @property + def verification_attempt_reference(self) -> str: + """Return the immutable verification-attempt receipt reference.""" + return self[8] + + @property + def verification_attempt_digest(self) -> str: + """Return the immutable verification-attempt receipt digest.""" + return self[9] + + @property + def owner_contract_reference(self) -> str: + """Return the released owner-contract reference.""" + return self[10] + + @property + def owner_contract_version(self) -> int: + """Return the positive released owner-contract version.""" + return self[11] + + @property + def owner_contract_digest(self) -> str: + """Return the immutable released owner-contract digest.""" + return self[12] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the governing owner contract became released authority.""" + return self[13] + + @property + def evaluated_at(self) -> datetime: + """Return when the evidence-verification attempt was evaluated.""" + return self[14] + + @property + def released_at(self) -> datetime: + """Return when this non-verifiability outcome became released evidence.""" + return self[15] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this outcome's owner-resolved authority.""" + return self[16] + + @property + def failed_evidence_released_at(self) -> datetime | None: + """Return when non-reproducible evidence became available for verification.""" + return self[17] + + @property + def verification_attempt_released_at(self) -> datetime: + """Return when the immutable verification-attempt receipt became released evidence.""" + return self[18] + + +class ValidationResultNonVerifiabilityView: + """Sealed non-authorizing outcome issued only after purpose-bound authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> ValidationResultNonVerifiabilityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "ValidationResultNonVerifiabilityView is issued only by " + "resolve_validation_result_nonverifiability." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("ValidationResultNonVerifiabilityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("ValidationResultNonVerifiabilityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_validation_result_nonverifiability_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable reason/evidence fields without scientific row values.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class ValidationResultNonVerifiabilityReadPort(Protocol): + """Owner read contract for released result non-verifiability evidence.""" + + def read_validation_result_nonverifiability( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + failed_evidence_reference: str | None, + failed_evidence_digest: str | None, + verification_attempt_reference: str, + verification_attempt_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultNonVerifiabilityRecord | None: + """Return matching released failure evidence or ``None`` through an owner ACL.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + ValidationResultNonVerifiabilityReadPort, + "read_validation_result_nonverifiability", +) + + +def _resolve_validation_result_nonverifiability_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + failed_evidence_reference: str | None = None, + failed_evidence_digest: str | None = None, + verification_attempt_reference: str, + verification_attempt_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultNonVerifiabilityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize then corroborate one exact released, non-authorizing verification attempt.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_validation_result_nonverifiability", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_validation_result_nonverifiability." + ) + + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + evidence_kind = _require_failed_evidence_kind(failed_evidence_kind) + mode = _require_failure_mode(failure_mode) + if mode == "missing": + if failed_evidence_reference is not None or failed_evidence_digest is not None: + raise ValueError( + "failed evidence reference and digest must be absent when evidence is missing." + ) + failed_reference = None + failed_digest = None + else: + if failed_evidence_reference is None or failed_evidence_digest is None: + raise ValueError( + "failed evidence reference and digest are required for non_reproducible lookup." + ) + failed_reference = _require_reference( + "failed_evidence_reference", + failed_evidence_reference, + _FAILED_REFERENCE_KIND_BY_EVIDENCE_KIND[evidence_kind], + ) + failed_digest = _require_digest("failed_evidence_digest", failed_evidence_digest) + attempt_ref = _require_reference( + "verification_attempt_reference", + verification_attempt_reference, + "validation_evidence_verification_attempt", + ) + attempt_digest = _require_digest( + "verification_attempt_digest", verification_attempt_digest + ) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=_restore_operational_uuid("tenant_record_id", tenant_identity), + validity_study_id=_restore_operational_uuid("validity_study_id", study_identity), + result_reference=result_ref, + result_digest=result_evidence_digest, + failed_evidence_kind=evidence_kind, + failure_mode=mode, + failed_evidence_reference=failed_reference, + failed_evidence_digest=failed_digest, + verification_attempt_reference=attempt_ref, + verification_attempt_digest=attempt_digest, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise ValidationResultNonVerifiabilityNotFound(str(study_id)) + if type(persisted) is not ValidationResultNonVerifiabilityRecord: + raise ValidationResultNonVerifiabilityIntegrityError( + "owner port returned non-canonical validation-result non-verifiability evidence" + ) + + try: + record = ValidationResultNonVerifiabilityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + result_reference=persisted.result_reference, + result_digest=persisted.result_digest, + failed_evidence_kind=persisted.failed_evidence_kind, + failure_mode=persisted.failure_mode, + failed_evidence_reference=persisted.failed_evidence_reference, + failed_evidence_digest=persisted.failed_evidence_digest, + verification_attempt_reference=persisted.verification_attempt_reference, + verification_attempt_digest=persisted.verification_attempt_digest, + verification_attempt_released_at=persisted.verification_attempt_released_at, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + evaluated_at=persisted.evaluated_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + failed_evidence_released_at=persisted.failed_evidence_released_at, + ) + except (IndexError, TypeError, ValueError) as exc: + raise ValidationResultNonVerifiabilityIntegrityError( + "owner port returned structurally invalid validation-result non-verifiability evidence" + ) from exc + if record != persisted: + raise ValidationResultNonVerifiabilityIntegrityError( + "owner port returned non-canonical validation-result non-verifiability structure" + ) + + requested_identity = ( + tenant_identity, + study_identity, + result_ref, + result_evidence_digest, + evidence_kind, + mode, + failed_reference, + failed_digest, + attempt_ref, + attempt_digest, + owner_ref, + owner_version, + owner_digest, + ) + record_identity = ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id), + _store_operational_uuid("record validity_study_id", record.validity_study_id), + record.result_reference, + record.result_digest, + record.failed_evidence_kind, + record.failure_mode, + record.failed_evidence_reference, + record.failed_evidence_digest, + record.verification_attempt_reference, + record.verification_attempt_digest, + record.owner_contract_reference, + record.owner_contract_version, + record.owner_contract_digest, + ) + if record_identity != requested_identity: + raise ValidationResultNonVerifiabilityIntegrityError( + "owner evidence does not match the requested non-verifiability attempt" + ) + if use_instant < record.released_at: + raise ValidationResultNonVerifiabilityIntegrityError( + "validation-result non-verifiability cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise ValidationResultNonVerifiabilityIntegrityError( + "validation-result non-verifiability ended at its owner-resolved supersession instant" + ) + + values = { + "result_reference": record.result_reference, + "result_digest": record.result_digest, + "verification_status": record.verification_status, + "failed_evidence_kind": record.failed_evidence_kind, + "failure_mode": record.failure_mode, + "failed_evidence_reference": record.failed_evidence_reference, + "failed_evidence_digest": record.failed_evidence_digest, + "failed_evidence_released_at": record.failed_evidence_released_at, + "verification_attempt_reference": record.verification_attempt_reference, + "verification_attempt_digest": record.verification_attempt_digest, + "verification_attempt_released_at": record.verification_attempt_released_at, + "owner_contract_reference": record.owner_contract_reference, + "owner_contract_version": record.owner_contract_version, + "owner_contract_digest": record.owner_contract_digest, + "owner_contract_released_at": record.owner_contract_released_at, + "evaluated_at": record.evaluated_at, + "released_at": record.released_at, + "superseded_at": record.superseded_at, + } + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return tenant_identity, study_identity, fields + + +def _build_validation_result_nonverifiability_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: ValidationResultNonVerifiabilityView) -> None: + """Verify one non-verifiability view against the private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise ValidationResultNonVerifiabilityIntegrityError( + "validation result non-verifiability view was not issued by " + "resolve_validation_result_nonverifiability" + ) from exc + if marker is not issuance_marker: + raise ValidationResultNonVerifiabilityIntegrityError( + "validation result non-verifiability view was not issued by " + "resolve_validation_result_nonverifiability" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + failed_evidence_reference: str | None = None, + failed_evidence_digest: str | None = None, + verification_attempt_reference: str, + verification_attempt_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultNonVerifiabilityReadPort, + ) -> ValidationResultNonVerifiabilityView: + """Authorize then issue one exact non-authorizing verification attempt.""" + tenant_identity, study_identity, fields = ( + _resolve_validation_result_nonverifiability_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + result_reference=result_reference, + result_digest=result_digest, + failed_evidence_kind=failed_evidence_kind, + failure_mode=failure_mode, + failed_evidence_reference=failed_evidence_reference, + failed_evidence_digest=failed_evidence_digest, + verification_attempt_reference=verification_attempt_reference, + verification_attempt_digest=verification_attempt_digest, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(ValidationResultNonVerifiabilityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_validation_result_nonverifiability_view_issued, + resolve_validation_result_nonverifiability, +) = _build_validation_result_nonverifiability_view_runtime() +del _build_validation_result_nonverifiability_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability_supersession_authority.py new file mode 100644 index 000000000..39e822259 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability_supersession_authority.py @@ -0,0 +1,700 @@ +"""Corroborate append-only supersession of released non-verifiability outcomes. + +A released ``not_verifiable`` outcome remains authoritative only until a new +immutable verification attempt re-evaluates the same result and failed-evidence +obligation. This boundary binds that successor attempt to the predecessor +cutover without exposing successor coordinates as reusable downstream authority. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .result_nonverifiability import ( + _require_failed_evidence_kind, + _require_failure_mode, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "validation_result_nonverifiability_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "verification_attempt_reference", + "verification_attempt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_target_result_reference", + "successor_target_result_digest", + "successor_failed_evidence_kind", + "successor_verification_attempt_reference", + "successor_verification_attempt_digest", + "successor_verification_attempt_released_at", + } +) +_VIEW_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "verification_attempt_reference", + "verification_attempt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class ValidationResultNonVerifiabilitySupersessionAuthorityNotFound(LookupError): + """Indicate that no released successor authority matches the negative outcome.""" + + +class ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that negative-outcome successor evidence cannot authorize use.""" + + +class ValidationResultNonVerifiabilitySupersessionAuthorityRecord(tuple): + """Immutable owner projection for one negative-outcome authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_target_result_reference: str | None = None, + successor_target_result_digest: str | None = None, + successor_failed_evidence_kind: str | None = None, + successor_verification_attempt_reference: str | None = None, + successor_verification_attempt_digest: str | None = None, + successor_verification_attempt_released_at: datetime | None = None, + ) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Validate predecessor chronology and one complete same-obligation successor attempt.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + evidence_kind = _require_failed_evidence_kind(failed_evidence_kind) + mode = _require_failure_mode(failure_mode) + if mode == "non_reproducible": + raise ValueError( + "non_reproducible supersession requires exact failed-evidence identity; " + "the v1 supersession contract fails closed instead of dropping it." + ) + attempt_ref = _require_reference( + "verification_attempt_reference", + verification_attempt_reference, + "validation_evidence_verification_attempt", + ) + attempt_digest = _require_digest( + "verification_attempt_digest", verification_attempt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_released = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_released > release_instant: + raise ValueError( + "owner contract must be released no later than non-verifiability outcome." + ) + if len({result_evidence_digest, attempt_digest, owner_digest}) != 3: + raise ValueError( + "result, verification-attempt, and owner-contract digests must be distinct." + ) + + successor_values = ( + superseded_at, + successor_target_result_reference, + successor_target_result_digest, + successor_failed_evidence_kind, + successor_verification_attempt_reference, + successor_verification_attempt_digest, + successor_verification_attempt_released_at, + ) + if all(value is None for value in successor_values): + cutover = None + successor_target_ref = None + successor_target_digest = None + successor_evidence_kind = None + successor_ref = None + successor_digest = None + successor_release = None + elif any(value is None for value in successor_values): + raise ValueError( + "non-verifiability supersession requires cutover, same-result and " + "failed-evidence-obligation binding, and complete successor attempt." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_target_ref = _require_reference( + "successor_target_result_reference", + successor_target_result_reference, + "validation_analysis_result", + ) + successor_target_digest = _require_digest( + "successor_target_result_digest", + successor_target_result_digest, + ) + successor_evidence_kind = _require_failed_evidence_kind( + successor_failed_evidence_kind + ) + successor_ref = _require_reference( + "successor_verification_attempt_reference", + successor_verification_attempt_reference, + "validation_evidence_verification_attempt", + ) + successor_digest = _require_digest( + "successor_verification_attempt_digest", + successor_verification_attempt_digest, + ) + successor_release = _require_aware_datetime( + "successor_verification_attempt_released_at", + successor_verification_attempt_released_at, + ) + if cutover <= release_instant: + raise ValueError( + "superseded_at must be later than non-verifiability release." + ) + if ( + successor_target_ref != result_ref + or successor_target_digest != result_evidence_digest + ): + raise ValueError( + "successor verification attempt must target the exact predecessor result." + ) + if successor_evidence_kind != evidence_kind: + raise ValueError( + "successor verification attempt must re-evaluate the same failed-evidence obligation." + ) + if successor_ref == attempt_ref: + raise ValueError("successor verification attempt must use a new reference.") + if successor_digest in {result_evidence_digest, attempt_digest, owner_digest}: + raise ValueError("successor verification attempt must identify new evidence.") + if successor_release != cutover: + raise ValueError( + "successor verification attempt must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("evidence_version", version), + ("failed_evidence_kind", evidence_kind), + ("failure_mode", mode), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", owner_released), + ("owner_contract_version", owner_version), + ("result_digest", result_evidence_digest), + ("result_reference", result_ref), + ("verification_attempt_digest", attempt_digest), + ("verification_attempt_reference", attempt_ref), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_failed_evidence_kind", successor_evidence_kind), + ("successor_target_result_digest", successor_target_digest), + ("successor_target_result_reference", successor_target_ref), + ("successor_verification_attempt_digest", successor_digest), + ("successor_verification_attempt_reference", successor_ref), + ("successor_verification_attempt_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable predecessor authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this non-verifiability outcome became released evidence.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this negative outcome's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return owner-internal successor obligation, attempt, and target-result coordinates.""" + return self[5] + + +class ValidationResultNonVerifiabilitySupersessionAuthorityView: + """Sealed predecessor authority issued only after purpose authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> ValidationResultNonVerifiabilitySupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "ValidationResultNonVerifiabilitySupersessionAuthorityView is issued only by " + "resolve_validation_result_nonverifiability_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError( + "ValidationResultNonVerifiabilitySupersessionAuthorityView is immutable." + ) + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError( + "ValidationResultNonVerifiabilitySupersessionAuthorityView is immutable." + ) + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_validation_result_nonverifiability_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return predecessor provenance without cutover or successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class ValidationResultNonVerifiabilitySupersessionAuthorityReadPort(Protocol): + """Owner read contract for released non-verifiability supersession evidence.""" + + def read_validation_result_nonverifiability_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord | None: + """Return matching released successor authority or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + ValidationResultNonVerifiabilitySupersessionAuthorityReadPort, + "read_validation_result_nonverifiability_supersession_authority", +) + + +def _resolve_validation_result_nonverifiability_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultNonVerifiabilitySupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve non-verifiability supersession into inert view state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), + "read_validation_result_nonverifiability_supersession_authority", + None, + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_validation_result_nonverifiability_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + evidence_kind = _require_failed_evidence_kind(failed_evidence_kind) + mode = _require_failure_mode(failure_mode) + attempt_ref = _require_reference( + "verification_attempt_reference", + verification_attempt_reference, + "validation_evidence_verification_attempt", + ) + attempt_digest = _require_digest( + "verification_attempt_digest", verification_attempt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + result_reference=result_ref, + result_digest=result_evidence_digest, + failed_evidence_kind=evidence_kind, + failure_mode=mode, + verification_attempt_reference=attempt_ref, + verification_attempt_digest=attempt_digest, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise ValidationResultNonVerifiabilitySupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "owner port returned non-canonical non-verifiability supersession evidence" + ) + + try: + persisted_fields = dict(persisted.fields) + persisted_successor = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = ValidationResultNonVerifiabilitySupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + result_reference=persisted_fields["result_reference"], + result_digest=persisted_fields["result_digest"], + failed_evidence_kind=persisted_fields["failed_evidence_kind"], + failure_mode=persisted_fields["failure_mode"], + verification_attempt_reference=persisted_fields["verification_attempt_reference"], + verification_attempt_digest=persisted_fields["verification_attempt_digest"], + evidence_version=persisted_fields["evidence_version"], + owner_contract_reference=persisted_fields["owner_contract_reference"], + owner_contract_version=persisted_fields["owner_contract_version"], + owner_contract_digest=persisted_fields["owner_contract_digest"], + owner_contract_released_at=persisted_fields["owner_contract_released_at"], + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_target_result_reference=( + None + if persisted_successor is None + else persisted_successor["successor_target_result_reference"] + ), + successor_target_result_digest=( + None + if persisted_successor is None + else persisted_successor["successor_target_result_digest"] + ), + successor_failed_evidence_kind=( + None + if persisted_successor is None + else persisted_successor["successor_failed_evidence_kind"] + ), + successor_verification_attempt_reference=( + None + if persisted_successor is None + else persisted_successor["successor_verification_attempt_reference"] + ), + successor_verification_attempt_digest=( + None + if persisted_successor is None + else persisted_successor["successor_verification_attempt_digest"] + ), + successor_verification_attempt_released_at=( + None + if persisted_successor is None + else persisted_successor["successor_verification_attempt_released_at"] + ), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "owner port returned structurally invalid non-verifiability supersession evidence" + ) from exc + if record != persisted: + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "owner port returned non-canonical non-verifiability supersession structure" + ) + + record_values = dict(record.fields) + requested_values = { + "evidence_version": version, + "failed_evidence_kind": evidence_kind, + "failure_mode": mode, + "owner_contract_digest": owner_digest, + "owner_contract_reference": owner_ref, + "owner_contract_version": owner_version, + "result_digest": result_evidence_digest, + "result_reference": result_ref, + "verification_attempt_digest": attempt_digest, + "verification_attempt_reference": attempt_ref, + } + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or any(record_values[name] != value for name, value in requested_values.items()) + ): + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "owner evidence does not match requested non-verifiability correction coordinates" + ) + if use_instant < record.released_at: + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "non-verifiability authority cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "non-verifiability authority ended at its owner-resolved supersession instant" + ) + + values = {**record_values, "released_at": record.released_at} + fields = tuple((field_name, values[field_name]) for field_name in sorted(_VIEW_FIELDS)) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_validation_result_nonverifiability_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued( + view: ValidationResultNonVerifiabilitySupersessionAuthorityView, + ) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "validation result non-verifiability supersession view was not issued by " + "resolve_validation_result_nonverifiability_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError( + "validation result non-verifiability supersession view was not issued by " + "resolve_validation_result_nonverifiability_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultNonVerifiabilitySupersessionAuthorityReadPort, + ) -> ValidationResultNonVerifiabilitySupersessionAuthorityView: + """Authorize then resolve one negative outcome's authority interval.""" + tenant_identity, study_identity, fields = ( + _resolve_validation_result_nonverifiability_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + result_reference=result_reference, + result_digest=result_digest, + failed_evidence_kind=failed_evidence_kind, + failure_mode=failure_mode, + verification_attempt_reference=verification_attempt_reference, + verification_attempt_digest=verification_attempt_digest, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(ValidationResultNonVerifiabilitySupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_validation_result_nonverifiability_supersession_view_issued, + resolve_validation_result_nonverifiability_supersession_authority, +) = _build_validation_result_nonverifiability_supersession_view_runtime() +del _build_validation_result_nonverifiability_supersession_view_runtime + + +__all__ = [ + "ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError", + "ValidationResultNonVerifiabilitySupersessionAuthorityNotFound", + "ValidationResultNonVerifiabilitySupersessionAuthorityReadPort", + "ValidationResultNonVerifiabilitySupersessionAuthorityRecord", + "ValidationResultNonVerifiabilitySupersessionAuthorityView", + "resolve_validation_result_nonverifiability_supersession_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability_supersession_v2_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability_supersession_v2_authority.py new file mode 100644 index 000000000..6e7aa5886 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_nonverifiability_supersession_v2_authority.py @@ -0,0 +1,686 @@ +"""Versioned exact-artifact correction authority for non-reproducible validation evidence.""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .result_nonverifiability import ( + ValidationResultNonVerifiabilityRecord, + _FAILED_REFERENCE_KIND_BY_EVIDENCE_KIND, + _require_failed_evidence_kind, +) +from .scientific_authority import _require_digest, _require_positive_integer, _require_reference + +_RESOURCE_KIND = "validation_result_nonverifiability_supersession_authority" +_OPERATION = "read" +_VERSION = 2 +_READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "failed_evidence_reference", + "failed_evidence_digest", + "failed_evidence_released_at", + "verification_attempt_reference", + "verification_attempt_digest", + "verification_attempt_released_at", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_target_result_reference", + "successor_target_result_digest", + "successor_failed_evidence_kind", + "successor_target_failed_evidence_reference", + "successor_target_failed_evidence_digest", + "successor_target_failed_evidence_released_at", + "successor_verification_attempt_reference", + "successor_verification_attempt_digest", + "successor_verification_attempt_released_at", + } +) +_VIEW_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "failed_evidence_reference", + "failed_evidence_digest", + "failed_evidence_released_at", + "verification_attempt_reference", + "verification_attempt_digest", + "verification_attempt_released_at", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class ValidationResultNonVerifiabilitySupersessionV2AuthorityNotFound(LookupError): + """Indicate that no released v2 correction authority matches the predecessor.""" + + +class ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError(RuntimeError): + """Indicate that released v2 correction evidence cannot authorize use.""" + + +def _predecessor_fields( + predecessor: ValidationResultNonVerifiabilityRecord, version: int +) -> tuple[tuple[str, object], ...]: + """Return immutable predecessor provenance plus the governed correction version.""" + return ( + ("evidence_version", version), + ("failed_evidence_digest", predecessor.failed_evidence_digest), + ("failed_evidence_kind", predecessor.failed_evidence_kind), + ("failed_evidence_reference", predecessor.failed_evidence_reference), + ("failed_evidence_released_at", predecessor.failed_evidence_released_at), + ("failure_mode", predecessor.failure_mode), + ("owner_contract_digest", predecessor.owner_contract_digest), + ("owner_contract_reference", predecessor.owner_contract_reference), + ("owner_contract_released_at", predecessor.owner_contract_released_at), + ("owner_contract_version", predecessor.owner_contract_version), + ("result_digest", predecessor.result_digest), + ("result_reference", predecessor.result_reference), + ("verification_attempt_digest", predecessor.verification_attempt_digest), + ("verification_attempt_reference", predecessor.verification_attempt_reference), + ("verification_attempt_released_at", predecessor.verification_attempt_released_at), + ) + + +def _revalidate_predecessor( + predecessor: ValidationResultNonVerifiabilityRecord, +) -> ValidationResultNonVerifiabilityRecord: + """Reconstruct the predecessor so tuple-level forgery cannot bypass owner invariants.""" + return ValidationResultNonVerifiabilityRecord( + tenant_record_id=predecessor.tenant_record_id, + validity_study_id=predecessor.validity_study_id, + result_reference=predecessor.result_reference, + result_digest=predecessor.result_digest, + failed_evidence_kind=predecessor.failed_evidence_kind, + failure_mode=predecessor.failure_mode, + failed_evidence_reference=predecessor.failed_evidence_reference, + failed_evidence_digest=predecessor.failed_evidence_digest, + failed_evidence_released_at=predecessor.failed_evidence_released_at, + verification_attempt_reference=predecessor.verification_attempt_reference, + verification_attempt_digest=predecessor.verification_attempt_digest, + verification_attempt_released_at=predecessor.verification_attempt_released_at, + owner_contract_reference=predecessor.owner_contract_reference, + owner_contract_version=predecessor.owner_contract_version, + owner_contract_digest=predecessor.owner_contract_digest, + owner_contract_released_at=predecessor.owner_contract_released_at, + evaluated_at=predecessor.evaluated_at, + released_at=predecessor.released_at, + superseded_at=predecessor.superseded_at, + ) + + +class ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord(tuple): + """Bind a non-reproducible predecessor to an exact-artifact successor attempt.""" + + __slots__ = () + + def __new__( + cls, + *, + predecessor: ValidationResultNonVerifiabilityRecord, + evidence_version: int, + superseded_at: datetime | None = None, + successor_target_result_reference: str | None = None, + successor_target_result_digest: str | None = None, + successor_failed_evidence_kind: str | None = None, + successor_target_failed_evidence_reference: str | None = None, + successor_target_failed_evidence_digest: str | None = None, + successor_target_failed_evidence_released_at: datetime | None = None, + successor_verification_attempt_reference: str | None = None, + successor_verification_attempt_digest: str | None = None, + successor_verification_attempt_released_at: datetime | None = None, + ) -> ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord: + """Validate exact predecessor provenance and an optional atomic successor cutover.""" + if type(predecessor) is not ValidationResultNonVerifiabilityRecord: + raise TypeError("predecessor must be an exact ValidationResultNonVerifiabilityRecord.") + predecessor = _revalidate_predecessor(predecessor) + if predecessor.failure_mode != "non_reproducible": + raise ValueError("v2 supersession is reserved for non_reproducible predecessors.") + version = _require_positive_integer("evidence_version", evidence_version) + if version != _VERSION: + raise ValueError("evidence_version must be 2 for exact-artifact supersession.") + failed_reference = predecessor.failed_evidence_reference + failed_digest = predecessor.failed_evidence_digest + failed_release = predecessor.failed_evidence_released_at + if failed_reference is None or failed_digest is None or failed_release is None: + raise ValueError( + "non_reproducible predecessor must retain exact failed-artifact evidence." + ) + + successor_values = ( + superseded_at, + successor_target_result_reference, + successor_target_result_digest, + successor_failed_evidence_kind, + successor_target_failed_evidence_reference, + successor_target_failed_evidence_digest, + successor_target_failed_evidence_released_at, + successor_verification_attempt_reference, + successor_verification_attempt_digest, + successor_verification_attempt_released_at, + ) + ordinary_cutover = predecessor.superseded_at + if all(value is None for value in successor_values): + if ordinary_cutover is not None: + raise ValueError( + "v2 correction coordinates must match the ordinary predecessor cutover." + ) + cutover = None + successor_fields = None + elif any(value is None for value in successor_values): + raise ValueError( + "v2 supersession requires cutover, exact predecessor target, exact failed artifact, " + "and complete successor verification-attempt evidence." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + if cutover <= predecessor.released_at: + raise ValueError("superseded_at must be later than predecessor release.") + if ordinary_cutover is None or cutover != ordinary_cutover: + raise ValueError( + "v2 supersession must equal the ordinary predecessor cutover." + ) + target_result_reference = _require_reference( + "successor_target_result_reference", + successor_target_result_reference, + "validation_analysis_result", + ) + target_result_digest = _require_digest( + "successor_target_result_digest", successor_target_result_digest + ) + target_kind = _require_failed_evidence_kind(successor_failed_evidence_kind) + target_failed_reference = _require_reference( + "successor_target_failed_evidence_reference", + successor_target_failed_evidence_reference, + _FAILED_REFERENCE_KIND_BY_EVIDENCE_KIND[predecessor.failed_evidence_kind], + ) + target_failed_digest = _require_digest( + "successor_target_failed_evidence_digest", + successor_target_failed_evidence_digest, + ) + target_failed_release = _require_aware_datetime( + "successor_target_failed_evidence_released_at", + successor_target_failed_evidence_released_at, + ) + successor_reference = _require_reference( + "successor_verification_attempt_reference", + successor_verification_attempt_reference, + "validation_evidence_verification_attempt", + ) + successor_digest = _require_digest( + "successor_verification_attempt_digest", + successor_verification_attempt_digest, + ) + successor_release = _require_aware_datetime( + "successor_verification_attempt_released_at", + successor_verification_attempt_released_at, + ) + if ( + target_result_reference != predecessor.result_reference + or target_result_digest != predecessor.result_digest + ): + raise ValueError("successor attempt must target the exact predecessor result.") + if target_kind != predecessor.failed_evidence_kind: + raise ValueError( + "successor attempt must re-evaluate the same failed-evidence family." + ) + if ( + target_failed_reference != failed_reference + or target_failed_digest != failed_digest + or target_failed_release != failed_release + ): + raise ValueError( + "successor attempt must target the exact failed artifact and release chronology." + ) + if successor_reference == predecessor.verification_attempt_reference: + raise ValueError("successor verification attempt must use a new reference.") + if successor_digest in { + predecessor.result_digest, + failed_digest, + predecessor.verification_attempt_digest, + predecessor.owner_contract_digest, + }: + raise ValueError( + "successor verification attempt must identify new evidence." + ) + if successor_release != cutover: + raise ValueError( + "successor verification attempt must be released exactly at supersession." + ) + successor_fields = ( + ("successor_failed_evidence_kind", target_kind), + ("successor_target_failed_evidence_digest", target_failed_digest), + ("successor_target_failed_evidence_reference", target_failed_reference), + ("successor_target_failed_evidence_released_at", target_failed_release), + ("successor_target_result_digest", target_result_digest), + ("successor_target_result_reference", target_result_reference), + ("successor_verification_attempt_digest", successor_digest), + ("successor_verification_attempt_reference", successor_reference), + ("successor_verification_attempt_released_at", successor_release), + ) + return tuple.__new__(cls, (predecessor, version, cutover, successor_fields)) + + @property + def predecessor(self) -> ValidationResultNonVerifiabilityRecord: + """Return the exact immutable predecessor owner record.""" + return self[0] + + @property + def evidence_version(self) -> int: + """Return the governed exact-artifact correction contract version.""" + return self[1] + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable predecessor provenance plus v2 evidence version.""" + return _predecessor_fields(self.predecessor, self.evidence_version) + + @property + def released_at(self) -> datetime: + """Return when the predecessor negative outcome became released evidence.""" + return self.predecessor.released_at + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of predecessor authority when a successor exists.""" + return self[2] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return owner-internal exact-artifact successor coordinates.""" + return self[3] + + +class ValidationResultNonVerifiabilitySupersessionV2AuthorityView: + """Sealed minimized predecessor provenance issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> ValidationResultNonVerifiabilitySupersessionV2AuthorityView: + """Reject public construction so only the resolver can issue an authorized view.""" + raise TypeError( + "ValidationResultNonVerifiabilitySupersessionV2AuthorityView is issued only by " + "resolve_validation_result_nonverifiability_supersession_v2_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError( + "ValidationResultNonVerifiabilitySupersessionV2AuthorityView is immutable." + ) + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError( + "ValidationResultNonVerifiabilitySupersessionV2AuthorityView is immutable." + ) + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_validation_result_nonverifiability_supersession_v2_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return predecessor provenance without cutover or successor coordinates.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort(Protocol): + """Read exact-artifact supersession evidence through the workforce-validation ACL.""" + + def read_validation_result_nonverifiability_supersession_v2_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failed_evidence_reference: str, + failed_evidence_digest: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord | None: + """Return one released v2 correction record or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort, + "read_validation_result_nonverifiability_supersession_v2_authority", +) + + +def _resolve_validation_result_nonverifiability_supersession_v2_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failed_evidence_reference: str, + failed_evidence_digest: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve exact-artifact supersession into inert view state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), + "read_validation_result_nonverifiability_supersession_v2_authority", + None, + ) + if type(read_capability) is not FunctionType or read_capability is _PROTOCOL_READ_CAPABILITY: + raise TypeError( + "read_port must expose a statically callable " + "read_validation_result_nonverifiability_supersession_v2_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", + _store_operational_uuid("validity_study_id", validity_study_id), + ) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + evidence_kind = _require_failed_evidence_kind(failed_evidence_kind) + failed_reference = _require_reference( + "failed_evidence_reference", + failed_evidence_reference, + _FAILED_REFERENCE_KIND_BY_EVIDENCE_KIND[evidence_kind], + ) + failed_digest = _require_digest("failed_evidence_digest", failed_evidence_digest) + attempt_ref = _require_reference( + "verification_attempt_reference", + verification_attempt_reference, + "validation_evidence_verification_attempt", + ) + attempt_digest = _require_digest( + "verification_attempt_digest", verification_attempt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != _VERSION: + raise ValueError("evidence_version must be 2 for exact-artifact supersession.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=_detach_policy(policy), + ) + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + result_reference=result_ref, + result_digest=result_evidence_digest, + failed_evidence_kind=evidence_kind, + failed_evidence_reference=failed_reference, + failed_evidence_digest=failed_digest, + verification_attempt_reference=attempt_ref, + verification_attempt_digest=attempt_digest, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityNotFound( + str(study_id) + ) + if type(persisted) is not ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "owner port returned non-canonical v2 non-verifiability supersession evidence" + ) + try: + successor_coordinates = dict(persisted.successor_fields or ()) + canonical = ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=persisted.predecessor, + evidence_version=persisted.evidence_version, + superseded_at=persisted.superseded_at, + **successor_coordinates, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "owner port returned structurally invalid v2 non-verifiability supersession evidence" + ) from exc + if canonical != persisted: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "owner port returned non-canonical v2 non-verifiability supersession structure" + ) + persisted = canonical + predecessor = persisted.predecessor + values = dict(persisted.fields) + requested_values = { + "evidence_version": version, + "failed_evidence_digest": failed_digest, + "failed_evidence_kind": evidence_kind, + "failed_evidence_reference": failed_reference, + "owner_contract_digest": owner_digest, + "owner_contract_reference": owner_ref, + "owner_contract_version": owner_version, + "result_digest": result_evidence_digest, + "result_reference": result_ref, + "verification_attempt_digest": attempt_digest, + "verification_attempt_reference": attempt_ref, + } + if ( + _store_operational_uuid( + "record tenant_record_id", predecessor.tenant_record_id + ) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid( + "record validity_study_id", predecessor.validity_study_id + ) + != _store_operational_uuid("requested validity_study_id", study_id) + or any(values[name] != value for name, value in requested_values.items()) + ): + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "owner evidence does not match requested v2 non-verifiability correction coordinates" + ) + if use_instant < persisted.released_at: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "v2 non-verifiability authority cannot be used before predecessor release" + ) + if persisted.superseded_at is not None and use_instant >= persisted.superseded_at: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "v2 non-verifiability authority ended at its owner-resolved supersession instant" + ) + projection_values = {**values, "released_at": persisted.released_at} + fields = tuple((name, projection_values[name]) for name in sorted(_VIEW_FIELDS)) + return ( + _store_operational_uuid("tenant_record_id", predecessor.tenant_record_id), + _store_operational_uuid("validity_study_id", predecessor.validity_study_id), + fields, + ) + + +def _build_validation_result_nonverifiability_supersession_v2_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued( + view: ValidationResultNonVerifiabilitySupersessionV2AuthorityView, + ) -> None: + """Verify one v2 supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "validation result non-verifiability supersession v2 view was not issued by " + "resolve_validation_result_nonverifiability_supersession_v2_authority" + ) from exc + if marker is not issuance_marker: + raise ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError( + "validation result non-verifiability supersession v2 view was not issued by " + "resolve_validation_result_nonverifiability_supersession_v2_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failed_evidence_reference: str, + failed_evidence_digest: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort, + ) -> ValidationResultNonVerifiabilitySupersessionV2AuthorityView: + """Authorize then resolve one exact-artifact correction interval.""" + tenant_identity, study_identity, fields = ( + _resolve_validation_result_nonverifiability_supersession_v2_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + result_reference=result_reference, + result_digest=result_digest, + failed_evidence_kind=failed_evidence_kind, + failed_evidence_reference=failed_evidence_reference, + failed_evidence_digest=failed_evidence_digest, + verification_attempt_reference=verification_attempt_reference, + verification_attempt_digest=verification_attempt_digest, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(ValidationResultNonVerifiabilitySupersessionV2AuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_validation_result_nonverifiability_supersession_v2_view_issued, + resolve_validation_result_nonverifiability_supersession_v2_authority, +) = _build_validation_result_nonverifiability_supersession_v2_view_runtime() +del _build_validation_result_nonverifiability_supersession_v2_view_runtime + + +__all__ = [ + "ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityNotFound", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityView", + "resolve_validation_result_nonverifiability_supersession_v2_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_supersession_authority.py new file mode 100644 index 000000000..08895fa5c --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/result_supersession_authority.py @@ -0,0 +1,591 @@ +"""Corroborate append-only validation-result correction authority. + +One validation-result record proves what immutable scientific evidence was +released. This application boundary proves when that released result remained +authoritative and which complete released successor ended its half-open +authority interval. Successor coordinates stay internal to the owner boundary. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "validation_result_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_result_reference", + "successor_correction_sequence", + "successor_result_digest", + "successor_released_at", + } +) +_VIEW_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class ValidationResultSupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the result version.""" + + +class ValidationResultSupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released correction evidence cannot authorize result use.""" + + +class ValidationResultSupersessionAuthorityRecord(tuple): + """Immutable owner projection for one released result authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_result_reference: str | None = None, + successor_correction_sequence: int | None = None, + successor_result_digest: str | None = None, + successor_released_at: datetime | None = None, + ) -> ValidationResultSupersessionAuthorityRecord: + """Validate released predecessor/successor chronology without result values.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + correction = _require_positive_integer("correction_sequence", correction_sequence) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + contract_released_at = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if contract_released_at > release_instant: + raise ValueError( + "owner contract must be released no later than validation result." + ) + + supersession_values = ( + superseded_at, + successor_result_reference, + successor_correction_sequence, + successor_result_digest, + successor_released_at, + ) + if all(value is None for value in supersession_values): + cutover = None + successor_ref = None + successor_correction = None + successor_digest = None + successor_release = None + elif any(value is None for value in supersession_values): + raise ValueError( + "result supersession requires time and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_result_reference", + successor_result_reference, + "validation_analysis_result", + ) + successor_correction = _require_positive_integer( + "successor_correction_sequence", successor_correction_sequence + ) + successor_digest = _require_digest( + "successor_result_digest", successor_result_digest + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover < release_instant: + raise ValueError("superseded_at cannot precede validation-result release.") + if successor_ref == result_ref: + raise ValueError("successor validation result must use a new reference.") + if successor_correction != correction + 1: + raise ValueError( + "successor correction_sequence must advance exactly by one." + ) + if successor_digest == result_evidence_digest: + raise ValueError("successor validation result must identify new evidence.") + if successor_release <= release_instant: + raise ValueError( + "successor validation result must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor validation result must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("correction_sequence", correction), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", contract_released_at), + ("owner_contract_version", owner_version), + ("result_digest", result_evidence_digest), + ("result_reference", result_ref), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_correction_sequence", successor_correction), + ("successor_released_at", successor_release), + ("successor_result_digest", successor_digest), + ("successor_result_reference", successor_ref), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-result authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this validation result became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this result's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class ValidationResultSupersessionAuthorityView: + """Sealed current-result authority issued only after purpose authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> ValidationResultSupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "ValidationResultSupersessionAuthorityView is issued only by " + "resolve_validation_result_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("ValidationResultSupersessionAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("ValidationResultSupersessionAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_validation_result_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return released current-result provenance without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class ValidationResultSupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released validation-result correction state.""" + + def read_validation_result_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultSupersessionAuthorityRecord | None: + """Return matching released supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + ValidationResultSupersessionAuthorityReadPort, + "read_validation_result_supersession_authority", +) + + +def _resolve_validation_result_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultSupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve validation-result supersession into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_validation_result_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_validation_result_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + result_ref = _require_reference( + "result_reference", result_reference, "validation_analysis_result" + ) + result_evidence_digest = _require_digest("result_digest", result_digest) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + correction = _require_positive_integer("correction_sequence", correction_sequence) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + result_reference=result_ref, + result_digest=result_evidence_digest, + evidence_version=version, + correction_sequence=correction, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise ValidationResultSupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not ValidationResultSupersessionAuthorityRecord: + raise ValidationResultSupersessionAuthorityIntegrityError( + "owner port returned non-canonical validation-result supersession evidence" + ) + + try: + persisted_fields = dict(persisted.fields) + persisted_successor = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = ValidationResultSupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + result_reference=persisted_fields["result_reference"], + result_digest=persisted_fields["result_digest"], + evidence_version=persisted_fields["evidence_version"], + correction_sequence=persisted_fields["correction_sequence"], + owner_contract_reference=persisted_fields["owner_contract_reference"], + owner_contract_version=persisted_fields["owner_contract_version"], + owner_contract_digest=persisted_fields["owner_contract_digest"], + owner_contract_released_at=persisted_fields["owner_contract_released_at"], + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_result_reference=( + None + if persisted_successor is None + else persisted_successor["successor_result_reference"] + ), + successor_correction_sequence=( + None + if persisted_successor is None + else persisted_successor["successor_correction_sequence"] + ), + successor_result_digest=( + None + if persisted_successor is None + else persisted_successor["successor_result_digest"] + ), + successor_released_at=( + None + if persisted_successor is None + else persisted_successor["successor_released_at"] + ), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise ValidationResultSupersessionAuthorityIntegrityError( + "owner port returned malformed validation-result supersession evidence" + ) from exc + if record != persisted: + raise ValidationResultSupersessionAuthorityIntegrityError( + "owner port returned non-canonical validation-result supersession structure" + ) + + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["result_reference"] != result_ref + or record_values["result_digest"] != result_evidence_digest + or record_values["evidence_version"] != version + or record_values["correction_sequence"] != correction + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise ValidationResultSupersessionAuthorityIntegrityError( + "owner evidence does not match requested validation-result correction coordinates" + ) + if use_instant < record.released_at: + raise ValidationResultSupersessionAuthorityIntegrityError( + "validation-result authority cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise ValidationResultSupersessionAuthorityIntegrityError( + "validation-result authority ended at its owner-resolved supersession instant" + ) + + values = { + **record_values, + "released_at": record.released_at, + } + fields = tuple((field_name, values[field_name]) for field_name in sorted(_VIEW_FIELDS)) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_validation_result_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: ValidationResultSupersessionAuthorityView) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise ValidationResultSupersessionAuthorityIntegrityError( + "validation result supersession view was not issued by " + "resolve_validation_result_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise ValidationResultSupersessionAuthorityIntegrityError( + "validation result supersession view was not issued by " + "resolve_validation_result_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: ValidationResultSupersessionAuthorityReadPort, + ) -> ValidationResultSupersessionAuthorityView: + """Authorize then resolve the validation-result authority interval.""" + tenant_identity, study_identity, fields = ( + _resolve_validation_result_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + result_reference=result_reference, + result_digest=result_digest, + evidence_version=evidence_version, + correction_sequence=correction_sequence, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(ValidationResultSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_validation_result_supersession_view_issued, + resolve_validation_result_supersession_authority, +) = _build_validation_result_supersession_view_runtime() +del _build_validation_result_supersession_view_runtime + + +__all__ = [ + "ValidationResultSupersessionAuthorityIntegrityError", + "ValidationResultSupersessionAuthorityNotFound", + "ValidationResultSupersessionAuthorityReadPort", + "ValidationResultSupersessionAuthorityRecord", + "ValidationResultSupersessionAuthorityView", + "resolve_validation_result_supersession_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/scientific_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/scientific_authority.py new file mode 100644 index 000000000..757457cb8 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/scientific_authority.py @@ -0,0 +1,710 @@ +"""Resolve purpose-bound scientific auxiliary-use authority through its owner port. + +This application boundary corroborates opaque calibration auxiliary coordinates +without copying protected auxiliary values or querying another bounded context's +application tables. It deliberately stops before durable PostgreSQL adoption: +the repository port must later be backed by released/versioned owner evidence. +The returned projection is data, not a reusable authorization credential. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +import re +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) + +_DIGEST_PATTERN = re.compile(r"^[0-9a-f]{64}$") +_REFERENCE_PATTERN = re.compile(r"^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$") +_RESOURCE_KIND = "calibration_auxiliary_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "authority_reference", + "auxiliary_projection_reference", + "auxiliary_projection_version", + "auxiliary_projection_digest", + "scientific_purpose_reference", + "scientific_purpose_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "authorization_receipt_reference", + "authorization_receipt_digest", + "authorization_receipt_released_at", + "scientific_use_receipt_reference", + "scientific_use_receipt_digest", + "scientific_use_at", + "authorized_from", + "authorized_to", + } +) + + +class CalibrationAuxiliaryAuthorityNotFound(LookupError): + """Indicate that no owner evidence corroborates the requested authority tuple.""" + + +class CalibrationAuxiliaryAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence does not match the authorized scientific use.""" + + +def _require_reference(field_name: str, value: object, namespace: str) -> str: + """Require one exact opaque namespaced reference without protected source values.""" + if ( + type(value) is not str + or _REFERENCE_PATTERN.fullmatch(value) is None + or value.partition(":")[0] != namespace + ): + raise ValueError(f"{field_name} must be an exact {namespace}: opaque reference.") + return value + + +def _require_digest(field_name: str, value: object) -> str: + """Require lowercase SHA-256 evidence rather than caller-readable source content.""" + if type(value) is not str or _DIGEST_PATTERN.fullmatch(value) is None: + raise ValueError(f"{field_name} must be lowercase SHA-256 hex.") + return value + + +def _require_positive_integer(field_name: str, value: object) -> int: + """Require a strict positive integer contract version without accepting booleans.""" + if type(value) is not int or value <= 0: + raise ValueError(f"{field_name} must be a positive integer.") + return value + + +class CalibrationAuxiliaryAuthorityRecord(tuple): + """Immutable owner projection corroborating one auxiliary-use authorization. + + Only opaque references, digests, versions, target identities, the exact + scientific-use instant, and its authorization interval cross this boundary. + Raw calibration attributes, benchmark values, protected characteristics, and + row-level weights remain behind their authoritative owners. + """ + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + scientific_purpose_reference: str, + scientific_purpose_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + authorization_receipt_reference: str, + authorization_receipt_digest: str, + authorization_receipt_released_at: datetime, + scientific_use_receipt_reference: str, + scientific_use_receipt_digest: str, + scientific_use_at: datetime, + authorized_from: datetime, + authorized_to: datetime | None, + ) -> CalibrationAuxiliaryAuthorityRecord: + """Validate and detach every authority-bearing scalar before tuple storage.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + authority_ref = _require_reference( + "authority_reference", authority_reference, "scientific_auxiliary_authority" + ) + projection_ref = _require_reference( + "auxiliary_projection_reference", + auxiliary_projection_reference, + "calibration_auxiliary_projection", + ) + projection_version = _require_positive_integer( + "auxiliary_projection_version", auxiliary_projection_version + ) + projection_digest = _require_digest( + "auxiliary_projection_digest", auxiliary_projection_digest + ) + purpose_ref = _require_reference( + "scientific_purpose_reference", + scientific_purpose_reference, + "scientific_data_use_purpose", + ) + purpose_digest = _require_digest("scientific_purpose_digest", scientific_purpose_digest) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_contract_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + authorization_ref = _require_reference( + "authorization_receipt_reference", + authorization_receipt_reference, + "scientific_data_authorization", + ) + authorization_digest = _require_digest( + "authorization_receipt_digest", authorization_receipt_digest + ) + authorization_release = _require_aware_datetime( + "authorization_receipt_released_at", authorization_receipt_released_at + ) + scientific_use_ref = _require_reference( + "scientific_use_receipt_reference", + scientific_use_receipt_reference, + "scientific_use_receipt", + ) + scientific_use_digest = _require_digest( + "scientific_use_receipt_digest", scientific_use_receipt_digest + ) + use_instant = _require_aware_datetime("scientific_use_at", scientific_use_at) + authorization_start = _require_aware_datetime("authorized_from", authorized_from) + authorization_end = ( + None + if authorized_to is None + else _require_aware_datetime("authorized_to", authorized_to) + ) + if owner_contract_release > authorization_start: + raise ValueError( + "owner contract must be released no later than authorized_from." + ) + if authorization_release < owner_contract_release: + raise ValueError( + "authorization receipt cannot predate owner contract release." + ) + if authorization_release > authorization_start: + raise ValueError( + "authorization receipt must be released no later than authorized_from." + ) + if authorization_end is not None and authorization_end <= authorization_start: + raise ValueError("authorized_to must be later than authorized_from.") + if use_instant < authorization_start or ( + authorization_end is not None and use_instant >= authorization_end + ): + raise ValueError( + "scientific_use_at must fall inside the authorization interval." + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + authority_ref, + projection_ref, + projection_version, + projection_digest, + purpose_ref, + purpose_digest, + owner_ref, + owner_version, + owner_digest, + owner_contract_release, + authorization_ref, + authorization_digest, + authorization_release, + scientific_use_ref, + scientific_use_digest, + use_instant, + authorization_start, + authorization_end, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this owner evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity bound to the scientific use.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def authority_reference(self) -> str: + """Return the opaque owner authority reference.""" + return self[2] + + @property + def auxiliary_projection_reference(self) -> str: + """Return the opaque purpose-limited auxiliary projection reference.""" + return self[3] + + @property + def auxiliary_projection_version(self) -> int: + """Return the positive version of the purpose-limited auxiliary projection.""" + return self[4] + + @property + def auxiliary_projection_digest(self) -> str: + """Return the projection evidence digest without exposing source attributes.""" + return self[5] + + @property + def scientific_purpose_reference(self) -> str: + """Return the governed scientific-use purpose reference.""" + return self[6] + + @property + def scientific_purpose_digest(self) -> str: + """Return the exact scientific-use purpose evidence digest.""" + return self[7] + + @property + def owner_contract_reference(self) -> str: + """Return the released owner-contract reference.""" + return self[8] + + @property + def owner_contract_version(self) -> int: + """Return the positive released owner-contract version.""" + return self[9] + + @property + def owner_contract_digest(self) -> str: + """Return the immutable bytes digest for the released owner contract.""" + return self[10] + + @property + def owner_contract_released_at(self) -> datetime: + """Return the owner-resolved contract release instant.""" + return self[11] + + @property + def authorization_receipt_reference(self) -> str: + """Return the authoritative scientific-use authorization receipt reference.""" + return self[12] + + @property + def authorization_receipt_digest(self) -> str: + """Return the authorization receipt digest used for exact correlation.""" + return self[13] + + @property + def authorization_receipt_released_at(self) -> datetime: + """Return the owner-resolved release instant of the authorization receipt.""" + return self[14] + + @property + def scientific_use_receipt_reference(self) -> str: + """Return the immutable scientific-use receipt reference.""" + return self[15] + + @property + def scientific_use_receipt_digest(self) -> str: + """Return the immutable scientific-use receipt digest.""" + return self[16] + + @property + def scientific_use_at(self) -> datetime: + """Return the owner-resolved UTC instant for the exact scientific use.""" + return self[17] + + @property + def authorized_from(self) -> datetime: + """Return the UTC instant when this scientific use became authorized.""" + return self[18] + + @property + def authorized_to(self) -> datetime | None: + """Return the exclusive UTC authorization end when one exists.""" + return self[19] + + +class CalibrationAuxiliaryAuthorityView: + """Sealed field-minimized data view issued only after owner resolution.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> CalibrationAuxiliaryAuthorityView: + """Reject public construction; the resolver is the only supported issuer.""" + raise TypeError( + "CalibrationAuxiliaryAuthorityView is issued only by " + "resolve_calibration_auxiliary_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("CalibrationAuxiliaryAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("CalibrationAuxiliaryAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject raw allocations not sealed by the authorized resolver path.""" + _require_calibration_auxiliary_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable corroborating authority fields without protected values.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class CalibrationAuxiliaryAuthorityReadPort(Protocol): + """Owner read contract for released calibration auxiliary-use authority evidence.""" + + def read_calibration_auxiliary_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + scientific_purpose_reference: str, + scientific_purpose_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + authorization_receipt_reference: str, + authorization_receipt_digest: str, + scientific_use_receipt_reference: str, + scientific_use_receipt_digest: str, + ) -> CalibrationAuxiliaryAuthorityRecord | None: + """Return matching released authority evidence or ``None`` through an owner ACL.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + CalibrationAuxiliaryAuthorityReadPort, "read_calibration_auxiliary_authority" +) + + +def _resolve_calibration_auxiliary_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + scientific_purpose_reference: str, + scientific_purpose_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + authorization_receipt_reference: str, + authorization_receipt_digest: str, + scientific_use_receipt_reference: str, + scientific_use_receipt_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationAuxiliaryAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and corroborate one exact calibration auxiliary-use authority tuple. + + The exact owner capability is captured inertly before authorization and the + same function is invoked afterward. The request carries no protected source + values. Owner evidence must reproduce every caller-supplied leaf coordinate, + including the projection reference/version/digest, receipt references and the + released owner-contract digest. Owner evidence must also prove that both the + governing contract and authorization receipt existed before the authorization + interval became effective; neither release instant is a caller coordinate. + The scientific-use receipt is independently bound to the same use instant + before any corroborating fields are returned. + """ + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_calibration_auxiliary_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_calibration_auxiliary_authority." + ) + + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + authority_ref = _require_reference( + "authority_reference", authority_reference, "scientific_auxiliary_authority" + ) + projection_ref = _require_reference( + "auxiliary_projection_reference", + auxiliary_projection_reference, + "calibration_auxiliary_projection", + ) + projection_version = _require_positive_integer( + "auxiliary_projection_version", auxiliary_projection_version + ) + projection_digest = _require_digest( + "auxiliary_projection_digest", auxiliary_projection_digest + ) + purpose_ref = _require_reference( + "scientific_purpose_reference", + scientific_purpose_reference, + "scientific_data_use_purpose", + ) + purpose_digest = _require_digest("scientific_purpose_digest", scientific_purpose_digest) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + authorization_ref = _require_reference( + "authorization_receipt_reference", + authorization_receipt_reference, + "scientific_data_authorization", + ) + authorization_digest = _require_digest( + "authorization_receipt_digest", authorization_receipt_digest + ) + scientific_use_ref = _require_reference( + "scientific_use_receipt_reference", + scientific_use_receipt_reference, + "scientific_use_receipt", + ) + scientific_use_digest = _require_digest( + "scientific_use_receipt_digest", scientific_use_receipt_digest + ) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=_restore_operational_uuid("tenant_record_id", tenant_identity), + validity_study_id=_restore_operational_uuid("validity_study_id", study_identity), + authority_reference=authority_ref, + auxiliary_projection_reference=projection_ref, + auxiliary_projection_version=projection_version, + auxiliary_projection_digest=projection_digest, + scientific_purpose_reference=purpose_ref, + scientific_purpose_digest=purpose_digest, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + authorization_receipt_reference=authorization_ref, + authorization_receipt_digest=authorization_digest, + scientific_use_receipt_reference=scientific_use_ref, + scientific_use_receipt_digest=scientific_use_digest, + ) + if persisted is None: + raise CalibrationAuxiliaryAuthorityNotFound(str(study_id)) + if type(persisted) is not CalibrationAuxiliaryAuthorityRecord: + raise CalibrationAuxiliaryAuthorityIntegrityError( + "owner port returned non-canonical calibration auxiliary authority evidence" + ) + + try: + record = CalibrationAuxiliaryAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + authority_reference=persisted.authority_reference, + auxiliary_projection_reference=persisted.auxiliary_projection_reference, + auxiliary_projection_version=persisted.auxiliary_projection_version, + auxiliary_projection_digest=persisted.auxiliary_projection_digest, + scientific_purpose_reference=persisted.scientific_purpose_reference, + scientific_purpose_digest=persisted.scientific_purpose_digest, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + authorization_receipt_reference=persisted.authorization_receipt_reference, + authorization_receipt_digest=persisted.authorization_receipt_digest, + authorization_receipt_released_at=persisted.authorization_receipt_released_at, + scientific_use_receipt_reference=persisted.scientific_use_receipt_reference, + scientific_use_receipt_digest=persisted.scientific_use_receipt_digest, + scientific_use_at=persisted.scientific_use_at, + authorized_from=persisted.authorized_from, + authorized_to=persisted.authorized_to, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise CalibrationAuxiliaryAuthorityIntegrityError( + "owner port returned malformed calibration auxiliary authority evidence" + ) from exc + if record != persisted: + raise CalibrationAuxiliaryAuthorityIntegrityError( + "owner port returned non-canonical calibration auxiliary authority evidence" + ) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != tenant_identity + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != study_identity + or record.authority_reference != authority_ref + or record.auxiliary_projection_reference != projection_ref + or record.auxiliary_projection_version != projection_version + or record.auxiliary_projection_digest != projection_digest + or record.scientific_purpose_reference != purpose_ref + or record.scientific_purpose_digest != purpose_digest + or record.owner_contract_reference != owner_ref + or record.owner_contract_version != owner_version + or record.owner_contract_digest != owner_digest + or record.authorization_receipt_reference != authorization_ref + or record.authorization_receipt_digest != authorization_digest + or record.scientific_use_receipt_reference != scientific_use_ref + or record.scientific_use_receipt_digest != scientific_use_digest + or record.scientific_use_at != use_instant + ): + raise CalibrationAuxiliaryAuthorityIntegrityError( + "owner evidence does not match the requested calibration auxiliary authority" + ) + + values = { + "authority_reference": record.authority_reference, + "auxiliary_projection_reference": record.auxiliary_projection_reference, + "auxiliary_projection_version": record.auxiliary_projection_version, + "auxiliary_projection_digest": record.auxiliary_projection_digest, + "scientific_purpose_reference": record.scientific_purpose_reference, + "scientific_purpose_digest": record.scientific_purpose_digest, + "owner_contract_reference": record.owner_contract_reference, + "owner_contract_version": record.owner_contract_version, + "owner_contract_digest": record.owner_contract_digest, + "owner_contract_released_at": record.owner_contract_released_at, + "authorization_receipt_reference": record.authorization_receipt_reference, + "authorization_receipt_digest": record.authorization_receipt_digest, + "authorization_receipt_released_at": record.authorization_receipt_released_at, + "scientific_use_receipt_reference": record.scientific_use_receipt_reference, + "scientific_use_receipt_digest": record.scientific_use_receipt_digest, + "scientific_use_at": record.scientific_use_at, + "authorized_from": record.authorized_from, + "authorized_to": record.authorized_to, + } + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return tenant_identity, study_identity, fields + + +def _build_calibration_auxiliary_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: CalibrationAuxiliaryAuthorityView) -> None: + """Verify one auxiliary view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise CalibrationAuxiliaryAuthorityIntegrityError( + "calibration auxiliary authority view was not issued by the resolver" + ) from exc + if marker is not issuance_marker: + raise CalibrationAuxiliaryAuthorityIntegrityError( + "calibration auxiliary authority view was not issued by the resolver" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + scientific_purpose_reference: str, + scientific_purpose_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + authorization_receipt_reference: str, + authorization_receipt_digest: str, + scientific_use_receipt_reference: str, + scientific_use_receipt_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: CalibrationAuxiliaryAuthorityReadPort, + ) -> CalibrationAuxiliaryAuthorityView: + """Authorize and corroborate one exact calibration auxiliary authority tuple.""" + tenant_identity, study_identity, fields = ( + _resolve_calibration_auxiliary_authority_state( + **{ + name: value + for name, value in locals().items() + if name != "issuance_marker" + } + ) + ) + view = object.__new__(CalibrationAuxiliaryAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_calibration_auxiliary_view_issued, + resolve_calibration_auxiliary_authority, +) = _build_calibration_auxiliary_view_runtime() +del _build_calibration_auxiliary_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/trimming_bounding_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/trimming_bounding_authority.py new file mode 100644 index 000000000..2edb93698 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/trimming_bounding_authority.py @@ -0,0 +1,634 @@ +"""Corroborate released trimming or bounding weight-adjustment evidence. + +This application boundary binds an exact governed trimming rule to the affected +case set and weight-artifact transition without copying case identities or +row-level weights. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "trimming_bounding_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "adjustment_receipt_reference", + "adjustment_receipt_digest", + "evidence_version", + "rule_reference", + "rule_version", + "rule_configuration_digest", + "affected_case_occurrence_set_digest", + "affected_case_count", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class TrimmingBoundingAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the adjustment receipt.""" + + +class TrimmingBoundingAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence cannot corroborate the requested adjustment.""" + + +class TrimmingBoundingAuthorityRecord(tuple): + """Immutable owner projection for one released trimming/bounding adjustment.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + rule_reference: str, + rule_version: int, + rule_configuration_digest: str, + affected_case_occurrence_set_digest: str, + affected_case_count: int, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> TrimmingBoundingAuthorityRecord: + """Validate and detach the minimum immutable trimming authority.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "adjustment_receipt_reference", + adjustment_receipt_reference, + "trimming_bounding_adjustment_receipt", + ) + receipt_digest = _require_digest( + "adjustment_receipt_digest", adjustment_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + rule_ref = _require_reference( + "rule_reference", rule_reference, "weight_trimming_rule" + ) + rule_ver = _require_positive_integer("rule_version", rule_version) + configuration = _require_digest( + "rule_configuration_digest", rule_configuration_digest + ) + affected_digest = _require_digest( + "affected_case_occurrence_set_digest", + affected_case_occurrence_set_digest, + ) + affected_count = _require_positive_integer( + "affected_case_count", affected_case_count + ) + input_digest = _require_digest( + "input_weight_artifact_digest", input_weight_artifact_digest + ) + output_digest = _require_digest( + "output_weight_artifact_digest", output_weight_artifact_digest + ) + if input_digest == output_digest: + raise ValueError( + "output_weight_artifact_digest must identify the adjusted weight artifact." + ) + constructed = _require_aware_datetime("constructed_at", constructed_at) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_released = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + supersession_instant = ( + None + if superseded_at is None + else _require_aware_datetime("superseded_at", superseded_at) + ) + if release_instant < constructed: + raise ValueError("released_at cannot precede constructed_at.") + if owner_released > release_instant: + raise ValueError( + "owner_contract_released_at cannot be later than released_at." + ) + if supersession_instant is not None and supersession_instant <= release_instant: + raise ValueError("superseded_at must be later than released_at.") + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + receipt_ref, + receipt_digest, + version, + rule_ref, + rule_ver, + configuration, + affected_digest, + affected_count, + input_digest, + output_digest, + constructed, + owner_ref, + owner_version, + owner_digest, + owner_released, + release_instant, + supersession_instant, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this released evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity for this released evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def adjustment_receipt_reference(self) -> str: + """Return the typed trimming/bounding receipt reference.""" + return self[2] + + @property + def adjustment_receipt_digest(self) -> str: + """Return the exact trimming/bounding receipt digest.""" + return self[3] + + @property + def evidence_version(self) -> int: + """Return the evidence version.""" + return self[4] + + @property + def rule_reference(self) -> str: + """Return the governed trimming-rule reference.""" + return self[5] + + @property + def rule_version(self) -> int: + """Return the governed trimming-rule version.""" + return self[6] + + @property + def rule_configuration_digest(self) -> str: + """Return the immutable trimming-rule configuration digest.""" + return self[7] + + @property + def affected_case_occurrence_set_digest(self) -> str: + """Return the exact affected-case occurrence-set digest.""" + return self[8] + + @property + def affected_case_count(self) -> int: + """Return the positive number of affected case occurrences.""" + return self[9] + + @property + def input_weight_artifact_digest(self) -> str: + """Return the input weight artifact digest.""" + return self[10] + + @property + def output_weight_artifact_digest(self) -> str: + """Return the adjusted output weight artifact digest.""" + return self[11] + + @property + def constructed_at(self) -> datetime: + """Return when the typed adjustment receipt was constructed.""" + return self[12] + + @property + def owner_contract_reference(self) -> str: + """Return the released owner-contract reference.""" + return self[13] + + @property + def owner_contract_version(self) -> int: + """Return the released owner-contract version.""" + return self[14] + + @property + def owner_contract_digest(self) -> str: + """Return the released owner-contract digest.""" + return self[15] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the governing owner contract became released authority.""" + return self[16] + + @property + def released_at(self) -> datetime: + """Return when this adjustment became released authority.""" + return self[17] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive owner-resolved cutover for this receipt.""" + return self[18] + + +class TrimmingBoundingAuthorityView: + """Field-minimized adjustment evidence issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> TrimmingBoundingAuthorityView: + """Reject direct construction; only the resolver may issue this view.""" + raise TypeError( + "TrimmingBoundingAuthorityView is issued only by " + "resolve_trimming_bounding_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Reject mutation after resolver-controlled issuance.""" + raise AttributeError("TrimmingBoundingAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Reject deletion after resolver-controlled issuance.""" + raise AttributeError("TrimmingBoundingAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Require the exact in-process marker written by the resolver.""" + _require_trimming_bounding_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable adjustment provenance without case identities.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class TrimmingBoundingAuthorityReadPort(Protocol): + """Owner read contract for released trimming/bounding adjustment evidence.""" + + def read_trimming_bounding_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + rule_reference: str, + rule_version: int, + rule_configuration_digest: str, + affected_case_occurrence_set_digest: str, + affected_case_count: int, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> TrimmingBoundingAuthorityRecord | None: + """Return matching released adjustment evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + TrimmingBoundingAuthorityReadPort, "read_trimming_bounding_authority" +) + + +def _coordinate_tuple(record: TrimmingBoundingAuthorityRecord) -> tuple[object, ...]: + """Return caller-known coordinates, excluding owner-resolved release instants.""" + return record[:16] + + +def _resolve_trimming_bounding_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + rule_reference: str, + rule_version: int, + rule_configuration_digest: str, + affected_case_occurrence_set_digest: str, + affected_case_count: int, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: TrimmingBoundingAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and corroborate exact released trimming/bounding evidence.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static(type(read_port), "read_trimming_bounding_authority", None) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable read_trimming_bounding_authority." + ) + + requested = TrimmingBoundingAuthorityRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + adjustment_receipt_reference=adjustment_receipt_reference, + adjustment_receipt_digest=adjustment_receipt_digest, + evidence_version=evidence_version, + rule_reference=rule_reference, + rule_version=rule_version, + rule_configuration_digest=rule_configuration_digest, + affected_case_occurrence_set_digest=affected_case_occurrence_set_digest, + affected_case_count=affected_case_count, + input_weight_artifact_digest=input_weight_artifact_digest, + output_weight_artifact_digest=output_weight_artifact_digest, + constructed_at=constructed_at, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + owner_contract_released_at=constructed_at, + released_at=constructed_at, + superseded_at=None, + ) + tenant_id = requested.tenant_record_id + study_id = requested.validity_study_id + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=requested.tenant_record_id, + validity_study_id=requested.validity_study_id, + adjustment_receipt_reference=requested.adjustment_receipt_reference, + adjustment_receipt_digest=requested.adjustment_receipt_digest, + evidence_version=requested.evidence_version, + rule_reference=requested.rule_reference, + rule_version=requested.rule_version, + rule_configuration_digest=requested.rule_configuration_digest, + affected_case_occurrence_set_digest=requested.affected_case_occurrence_set_digest, + affected_case_count=requested.affected_case_count, + input_weight_artifact_digest=requested.input_weight_artifact_digest, + output_weight_artifact_digest=requested.output_weight_artifact_digest, + constructed_at=requested.constructed_at, + owner_contract_reference=requested.owner_contract_reference, + owner_contract_version=requested.owner_contract_version, + owner_contract_digest=requested.owner_contract_digest, + ) + if persisted is None: + raise TrimmingBoundingAuthorityNotFound(str(study_id)) + if type(persisted) is not TrimmingBoundingAuthorityRecord: + raise TrimmingBoundingAuthorityIntegrityError( + "owner port returned non-canonical trimming/bounding authority evidence" + ) + + try: + record = TrimmingBoundingAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + adjustment_receipt_reference=persisted.adjustment_receipt_reference, + adjustment_receipt_digest=persisted.adjustment_receipt_digest, + evidence_version=persisted.evidence_version, + rule_reference=persisted.rule_reference, + rule_version=persisted.rule_version, + rule_configuration_digest=persisted.rule_configuration_digest, + affected_case_occurrence_set_digest=persisted.affected_case_occurrence_set_digest, + affected_case_count=persisted.affected_case_count, + input_weight_artifact_digest=persisted.input_weight_artifact_digest, + output_weight_artifact_digest=persisted.output_weight_artifact_digest, + constructed_at=persisted.constructed_at, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise TrimmingBoundingAuthorityIntegrityError( + "owner port returned malformed trimming/bounding authority evidence" + ) from exc + if record != persisted: + raise TrimmingBoundingAuthorityIntegrityError( + "owner port returned non-canonical trimming/bounding authority evidence" + ) + if _coordinate_tuple(record) != _coordinate_tuple(requested): + raise TrimmingBoundingAuthorityIntegrityError( + "released trimming/bounding authority does not match requested coordinates" + ) + if record.released_at > use_instant: + raise TrimmingBoundingAuthorityIntegrityError( + "trimming/bounding evidence must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise TrimmingBoundingAuthorityIntegrityError( + "trimming/bounding evidence is superseded for this scientific-use instant" + ) + + fields: tuple[tuple[str, object], ...] = ( + ("adjustment_receipt_digest", record.adjustment_receipt_digest), + ("adjustment_receipt_reference", record.adjustment_receipt_reference), + ("affected_case_count", record.affected_case_count), + ("affected_case_occurrence_set_digest", record.affected_case_occurrence_set_digest), + ("constructed_at", record.constructed_at), + ("evidence_version", record.evidence_version), + ("input_weight_artifact_digest", record.input_weight_artifact_digest), + ("output_weight_artifact_digest", record.output_weight_artifact_digest), + ("owner_contract_digest", record.owner_contract_digest), + ("owner_contract_reference", record.owner_contract_reference), + ("owner_contract_released_at", record.owner_contract_released_at), + ("owner_contract_version", record.owner_contract_version), + ("released_at", record.released_at), + ("rule_configuration_digest", record.rule_configuration_digest), + ("rule_reference", record.rule_reference), + ("rule_version", record.rule_version), + ("superseded_at", record.superseded_at), + ) + return ( + _store_operational_uuid("tenant_record_id", tenant_id), + _store_operational_uuid("validity_study_id", study_id), + fields, + ) + + +def _build_trimming_bounding_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: TrimmingBoundingAuthorityView) -> None: + """Verify one trimming/bounding view against the private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise TrimmingBoundingAuthorityIntegrityError( + "trimming/bounding authority view was not issued by the resolver" + ) from exc + if marker is not issuance_marker: + raise TrimmingBoundingAuthorityIntegrityError( + "trimming/bounding authority view has an invalid issuance marker" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + rule_reference: str, + rule_version: int, + rule_configuration_digest: str, + affected_case_occurrence_set_digest: str, + affected_case_count: int, + input_weight_artifact_digest: str, + output_weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: TrimmingBoundingAuthorityReadPort, + ) -> TrimmingBoundingAuthorityView: + """Authorize then issue exact released trimming/bounding evidence.""" + tenant_identity, study_identity, fields = ( + _resolve_trimming_bounding_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + adjustment_receipt_reference=adjustment_receipt_reference, + adjustment_receipt_digest=adjustment_receipt_digest, + evidence_version=evidence_version, + rule_reference=rule_reference, + rule_version=rule_version, + rule_configuration_digest=rule_configuration_digest, + affected_case_occurrence_set_digest=affected_case_occurrence_set_digest, + affected_case_count=affected_case_count, + input_weight_artifact_digest=input_weight_artifact_digest, + output_weight_artifact_digest=output_weight_artifact_digest, + constructed_at=constructed_at, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(TrimmingBoundingAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_trimming_bounding_view_issued, + resolve_trimming_bounding_authority, +) = _build_trimming_bounding_view_runtime() +del _build_trimming_bounding_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/trimming_bounding_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/trimming_bounding_supersession_authority.py new file mode 100644 index 000000000..aa9f60b55 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/trimming_bounding_supersession_authority.py @@ -0,0 +1,561 @@ +"""Corroborate append-only trimming/bounding correction authority. + +The ordinary trimming/bounding projection proves which governed adjustment +receipt produced a released weight artifact. This boundary proves when that +receipt remained authoritative and which released successor ended its half-open +authority interval without exposing case identities or row-level weights. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "trimming_bounding_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "adjustment_receipt_reference", + "adjustment_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_adjustment_receipt_reference", + "successor_adjustment_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class TrimmingBoundingSupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the adjustment receipt.""" + + +class TrimmingBoundingSupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released trimming correction evidence cannot authorize use.""" + + +class TrimmingBoundingSupersessionAuthorityRecord(tuple): + """Immutable owner projection for one trimming receipt authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_adjustment_receipt_reference: str | None = None, + successor_adjustment_receipt_digest: str | None = None, + successor_evidence_version: int | None = None, + successor_released_at: datetime | None = None, + ) -> TrimmingBoundingSupersessionAuthorityRecord: + """Validate one released predecessor and its optional atomic successor edge.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "adjustment_receipt_reference", + adjustment_receipt_reference, + "trimming_bounding_adjustment_receipt", + ) + receipt_digest = _require_digest( + "adjustment_receipt_digest", adjustment_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release > release_instant: + raise ValueError( + "owner contract must be released no later than trimming/bounding receipt." + ) + + successor_values = ( + superseded_at, + successor_adjustment_receipt_reference, + successor_adjustment_receipt_digest, + successor_evidence_version, + successor_released_at, + ) + if all(value is None for value in successor_values): + cutover = None + successor_ref = None + successor_digest = None + successor_version = None + successor_release = None + elif any(value is None for value in successor_values): + raise ValueError( + "trimming supersession requires cutover and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_adjustment_receipt_reference", + successor_adjustment_receipt_reference, + "trimming_bounding_adjustment_receipt", + ) + successor_digest = _require_digest( + "successor_adjustment_receipt_digest", + successor_adjustment_receipt_digest, + ) + successor_version = _require_positive_integer( + "successor_evidence_version", successor_evidence_version + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover <= release_instant: + raise ValueError("superseded_at must be later than trimming receipt release.") + if successor_ref == receipt_ref: + raise ValueError("successor trimming receipt must have a new reference.") + if successor_digest == receipt_digest: + raise ValueError("successor trimming receipt must identify new evidence.") + if successor_version != 1: + raise ValueError("successor_evidence_version must remain 1.") + if successor_release <= release_instant: + raise ValueError( + "successor trimming receipt must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor trimming receipt must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("adjustment_receipt_digest", receipt_digest), + ("adjustment_receipt_reference", receipt_ref), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", owner_release), + ("owner_contract_version", owner_version), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_adjustment_receipt_digest", successor_digest), + ("successor_adjustment_receipt_reference", successor_ref), + ("successor_evidence_version", successor_version), + ("successor_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-receipt authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this trimming receipt became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this receipt's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class TrimmingBoundingSupersessionAuthorityView: + """Sealed current-receipt authority issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> TrimmingBoundingSupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "TrimmingBoundingSupersessionAuthorityView is issued only by " + "resolve_trimming_bounding_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("TrimmingBoundingSupersessionAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("TrimmingBoundingSupersessionAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_trimming_bounding_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return current receipt authority without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class TrimmingBoundingSupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released trimming correction state.""" + + def read_trimming_bounding_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> TrimmingBoundingSupersessionAuthorityRecord | None: + """Return matching released trimming supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + TrimmingBoundingSupersessionAuthorityReadPort, + "read_trimming_bounding_supersession_authority", +) + + +def _resolve_trimming_bounding_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: TrimmingBoundingSupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve trimming supersession into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_trimming_bounding_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_trimming_bounding_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + receipt_ref = _require_reference( + "adjustment_receipt_reference", + adjustment_receipt_reference, + "trimming_bounding_adjustment_receipt", + ) + receipt_digest = _require_digest( + "adjustment_receipt_digest", adjustment_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + adjustment_receipt_reference=receipt_ref, + adjustment_receipt_digest=receipt_digest, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise TrimmingBoundingSupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not TrimmingBoundingSupersessionAuthorityRecord: + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "owner port returned non-canonical trimming supersession evidence" + ) + + try: + successor_values = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = TrimmingBoundingSupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_adjustment_receipt_reference=( + None + if successor_values is None + else successor_values["successor_adjustment_receipt_reference"] + ), + successor_adjustment_receipt_digest=( + None + if successor_values is None + else successor_values["successor_adjustment_receipt_digest"] + ), + successor_evidence_version=( + None + if successor_values is None + else successor_values["successor_evidence_version"] + ), + successor_released_at=( + None if successor_values is None else successor_values["successor_released_at"] + ), + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "owner port returned malformed trimming supersession evidence" + ) from exc + if record != persisted: + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "owner port returned non-canonical trimming supersession evidence" + ) + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["adjustment_receipt_reference"] != receipt_ref + or record_values["adjustment_receipt_digest"] != receipt_digest + or record_values["evidence_version"] != version + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "released trimming supersession authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "trimming receipt must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "trimming receipt is superseded for this scientific-use instant" + ) + + view_values = dict(record.fields) + view_values["released_at"] = record.released_at + fields = tuple( + (name, view_values[name]) + for name in ( + "adjustment_receipt_digest", + "adjustment_receipt_reference", + "evidence_version", + "owner_contract_digest", + "owner_contract_reference", + "owner_contract_released_at", + "owner_contract_version", + "released_at", + ) + ) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_trimming_bounding_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: TrimmingBoundingSupersessionAuthorityView) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "trimming/bounding supersession view was not issued by " + "resolve_trimming_bounding_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise TrimmingBoundingSupersessionAuthorityIntegrityError( + "trimming/bounding supersession view was not issued by " + "resolve_trimming_bounding_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + adjustment_receipt_reference: str, + adjustment_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: TrimmingBoundingSupersessionAuthorityReadPort, + ) -> TrimmingBoundingSupersessionAuthorityView: + """Authorize then resolve the trimming receipt authority interval.""" + tenant_identity, study_identity, fields = ( + _resolve_trimming_bounding_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + adjustment_receipt_reference=adjustment_receipt_reference, + adjustment_receipt_digest=adjustment_receipt_digest, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(TrimmingBoundingSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_trimming_bounding_supersession_view_issued, + resolve_trimming_bounding_supersession_authority, +) = _build_trimming_bounding_supersession_view_runtime() +del _build_trimming_bounding_supersession_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/variance_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/variance_authority.py new file mode 100644 index 000000000..577043e79 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/variance_authority.py @@ -0,0 +1,785 @@ +"""Corroborate point-weight and variance-design compatibility through an owner port. + +This application boundary keeps released sampling, point-weight, and variance +coordinates correlated without importing mutable scientific-package source or +copying row-level weights, replicate vectors, frame variables, or protected +attributes. The persistence child must later back the port with released, +versioned owner evidence. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +import re +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) + +_DIGEST_PATTERN = re.compile(r"^[0-9a-f]{64}$") +_REFERENCE_PATTERN = re.compile(r"^[a-z][a-z0-9_]*:[A-Za-z0-9][A-Za-z0-9._~-]*$") +_RESOURCE_KIND = "weight_variance_authority" +_OPERATION = "read" +_VARIANCE_EVIDENCE_MODES = frozenset( + { + "joint_inclusion", + "reproducible_design_algorithm", + "replicate_weights", + "approximation", + } +) +_VARIANCE_SEMANTICS = frozenset({"exact", "approximate"}) +_READ_FIELDS = frozenset( + { + "authority_reference", + "sampling_receipt_reference", + "sampling_receipt_version", + "sampling_receipt_digest", + "analysis_weight_receipt_digest", + "analytic_case_occurrence_set_digest", + "weight_eligibility_receipt_digest", + "weight_correction_sequence", + "final_weight_artifact_digest", + "variance_design_receipt_reference", + "variance_design_receipt_version", + "variance_design_receipt_digest", + "variance_method_reference", + "variance_method_version", + "variance_evidence_mode", + "variance_semantics", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class WeightVarianceAuthorityNotFound(LookupError): + """Indicate that no owner evidence corroborates the requested compatibility tuple.""" + + +class WeightVarianceAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence cannot support the requested scientific binding.""" + + +def _require_reference(field_name: str, value: object, namespace: str) -> str: + """Require one exact opaque namespaced evidence reference.""" + if ( + type(value) is not str + or _REFERENCE_PATTERN.fullmatch(value) is None + or value.partition(":")[0] != namespace + ): + raise ValueError(f"{field_name} must be an exact {namespace}: opaque reference.") + return value + + +def _require_digest(field_name: str, value: object) -> str: + """Require lowercase SHA-256 evidence rather than caller-readable source content.""" + if type(value) is not str or _DIGEST_PATTERN.fullmatch(value) is None: + raise ValueError(f"{field_name} must be lowercase SHA-256 hex.") + return value + + +def _require_positive_integer(field_name: str, value: object) -> int: + """Require a strict positive integer contract version without accepting booleans.""" + if type(value) is not int or value <= 0: + raise ValueError(f"{field_name} must be a positive integer.") + return value + + +def _require_variance_evidence_mode(value: object) -> str: + """Require one controlled #406 variance-evidence strategy identifier.""" + if type(value) is not str or value not in _VARIANCE_EVIDENCE_MODES: + raise ValueError("variance_evidence_mode must be a supported controlled value.") + return value + + +def _require_variance_semantics(value: object) -> str: + """Require explicit exact-versus-approximate uncertainty semantics.""" + if type(value) is not str or value not in _VARIANCE_SEMANTICS: + raise ValueError("variance_semantics must be exact or approximate.") + return value + + +class WeightVarianceAuthorityRecord(tuple): + """Immutable owner projection proving point/variance evidence compatibility.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + sampling_receipt_reference: str, + sampling_receipt_version: int, + sampling_receipt_digest: str, + analysis_weight_receipt_digest: str, + analytic_case_occurrence_set_digest: str, + weight_eligibility_receipt_digest: str, + weight_correction_sequence: int, + final_weight_artifact_digest: str, + variance_design_receipt_reference: str, + variance_design_receipt_version: int, + variance_design_receipt_digest: str, + variance_method_reference: str, + variance_method_version: int, + variance_evidence_mode: str, + variance_semantics: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> WeightVarianceAuthorityRecord: + """Validate and detach the minimum immutable compatibility coordinates.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + authority_ref = _require_reference( + "authority_reference", authority_reference, "variance_compatibility_authority" + ) + sampling_ref = _require_reference( + "sampling_receipt_reference", sampling_receipt_reference, "sampling_design_receipt" + ) + sampling_version = _require_positive_integer( + "sampling_receipt_version", sampling_receipt_version + ) + sampling_digest = _require_digest("sampling_receipt_digest", sampling_receipt_digest) + point_digest = _require_digest( + "analysis_weight_receipt_digest", analysis_weight_receipt_digest + ) + case_digest = _require_digest( + "analytic_case_occurrence_set_digest", analytic_case_occurrence_set_digest + ) + eligibility_digest = _require_digest( + "weight_eligibility_receipt_digest", weight_eligibility_receipt_digest + ) + correction_sequence = _require_positive_integer( + "weight_correction_sequence", weight_correction_sequence + ) + final_digest = _require_digest("final_weight_artifact_digest", final_weight_artifact_digest) + variance_ref = _require_reference( + "variance_design_receipt_reference", + variance_design_receipt_reference, + "variance_design_receipt", + ) + variance_version = _require_positive_integer( + "variance_design_receipt_version", variance_design_receipt_version + ) + variance_digest = _require_digest( + "variance_design_receipt_digest", variance_design_receipt_digest + ) + if variance_digest == point_digest: + raise ValueError( + "variance_design_receipt_digest must identify evidence distinct from the analysis weight receipt." + ) + method_ref = _require_reference( + "variance_method_reference", variance_method_reference, "variance_method" + ) + method_version = _require_positive_integer("variance_method_version", variance_method_version) + evidence_mode = _require_variance_evidence_mode(variance_evidence_mode) + semantics = _require_variance_semantics(variance_semantics) + if evidence_mode == "approximation" and semantics != "approximate": + raise ValueError("approximation evidence must declare approximate variance semantics.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release_instant = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release_instant > release_instant: + raise ValueError( + "owner contract must be released no later than the compatibility authority" + ) + supersession_instant = None + if superseded_at is not None: + supersession_instant = _require_aware_datetime("superseded_at", superseded_at) + if supersession_instant <= release_instant: + raise ValueError("superseded_at must be later than released_at") + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + authority_ref, + sampling_ref, + sampling_version, + sampling_digest, + point_digest, + case_digest, + eligibility_digest, + correction_sequence, + final_digest, + variance_ref, + variance_version, + variance_digest, + method_ref, + method_version, + evidence_mode, + semantics, + owner_ref, + owner_version, + owner_digest, + release_instant, + owner_release_instant, + supersession_instant, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for the authority evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity for the authority evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def authority_reference(self) -> str: + """Return the opaque compatibility-authority reference.""" + return self[2] + + @property + def sampling_receipt_reference(self) -> str: + """Return the released #405 sampling receipt reference.""" + return self[3] + + @property + def sampling_receipt_version(self) -> int: + """Return the released sampling receipt version.""" + return self[4] + + @property + def sampling_receipt_digest(self) -> str: + """Return the released sampling receipt digest.""" + return self[5] + + @property + def analysis_weight_receipt_digest(self) -> str: + """Return the exact final point-weight receipt digest.""" + return self[6] + + @property + def analytic_case_occurrence_set_digest(self) -> str: + """Return the exact ordered analytic-case occurrence-set digest.""" + return self[7] + + @property + def weight_eligibility_receipt_digest(self) -> str: + """Return the exact point-weight eligibility receipt digest.""" + return self[8] + + @property + def weight_correction_sequence(self) -> int: + """Return the append-only point-weight correction sequence.""" + return self[9] + + @property + def final_weight_artifact_digest(self) -> str: + """Return the final point-weight artifact digest used by variance evidence.""" + return self[10] + + @property + def variance_design_receipt_reference(self) -> str: + """Return the released #406 variance-design receipt reference.""" + return self[11] + + @property + def variance_design_receipt_version(self) -> int: + """Return the released variance-design receipt version.""" + return self[12] + + @property + def variance_design_receipt_digest(self) -> str: + """Return the released variance-design receipt digest.""" + return self[13] + + @property + def variance_method_reference(self) -> str: + """Return the controlled variance-method reference.""" + return self[14] + + @property + def variance_method_version(self) -> int: + """Return the controlled variance-method version.""" + return self[15] + + @property + def variance_evidence_mode(self) -> str: + """Return the controlled #406 evidence strategy.""" + return self[16] + + @property + def variance_semantics(self) -> str: + """Return exact-versus-approximate uncertainty semantics.""" + return self[17] + + @property + def owner_contract_reference(self) -> str: + """Return the released owner-contract reference.""" + return self[18] + + @property + def owner_contract_version(self) -> int: + """Return the positive released owner-contract version.""" + return self[19] + + @property + def owner_contract_digest(self) -> str: + """Return the immutable released owner-contract digest.""" + return self[20] + + @property + def released_at(self) -> datetime: + """Return the owner-resolved release instant for this authority evidence.""" + return self[21] + + @property + def owner_contract_released_at(self) -> datetime: + """Return the owner-resolved release instant for the governing owner contract.""" + return self[22] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive owner-resolved cutover instant, when one exists.""" + return self[23] + + +class WeightVarianceAuthorityView: + """Sealed compatibility evidence issued only after purpose-bound authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> WeightVarianceAuthorityView: + """Reject public construction; the resolver is the only supported issuer.""" + raise TypeError( + "WeightVarianceAuthorityView is issued only by resolve_weight_variance_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("WeightVarianceAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("WeightVarianceAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_weight_variance_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable corroborating fields without row-level scientific data.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class WeightVarianceAuthorityReadPort(Protocol): + """Owner read contract for released #405/#406/#407 compatibility evidence.""" + + def read_weight_variance_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + sampling_receipt_reference: str, + sampling_receipt_version: int, + sampling_receipt_digest: str, + analysis_weight_receipt_digest: str, + analytic_case_occurrence_set_digest: str, + weight_eligibility_receipt_digest: str, + weight_correction_sequence: int, + final_weight_artifact_digest: str, + variance_design_receipt_reference: str, + variance_design_receipt_version: int, + variance_design_receipt_digest: str, + variance_method_reference: str, + variance_method_version: int, + variance_evidence_mode: str, + variance_semantics: str, + owner_contract_reference: str, + owner_contract_version: int, + ) -> WeightVarianceAuthorityRecord | None: + """Return matching released authority evidence or ``None`` through an owner ACL.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + WeightVarianceAuthorityReadPort, "read_weight_variance_authority" +) + + +def _resolve_weight_variance_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + sampling_receipt_reference: str, + sampling_receipt_version: int, + sampling_receipt_digest: str, + analysis_weight_receipt_digest: str, + analytic_case_occurrence_set_digest: str, + weight_eligibility_receipt_digest: str, + weight_correction_sequence: int, + final_weight_artifact_digest: str, + variance_design_receipt_reference: str, + variance_design_receipt_version: int, + variance_design_receipt_digest: str, + variance_method_reference: str, + variance_method_version: int, + variance_evidence_mode: str, + variance_semantics: str, + owner_contract_reference: str, + owner_contract_version: int, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightVarianceAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and corroborate point/variance compatibility into inert view state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static(type(read_port), "read_weight_variance_authority", None) + if type(read_capability) is not FunctionType or read_capability is _PROTOCOL_READ_CAPABILITY: + raise TypeError( + "read_port must expose a statically callable read_weight_variance_authority." + ) + + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + tenant_id = _restore_operational_uuid("tenant_record_id", tenant_identity) + study_id = _restore_operational_uuid("validity_study_id", study_identity) + sampling_ref = _require_reference( + "sampling_receipt_reference", sampling_receipt_reference, "sampling_design_receipt" + ) + sampling_version = _require_positive_integer( + "sampling_receipt_version", sampling_receipt_version + ) + sampling_digest = _require_digest("sampling_receipt_digest", sampling_receipt_digest) + point_digest = _require_digest("analysis_weight_receipt_digest", analysis_weight_receipt_digest) + case_digest = _require_digest( + "analytic_case_occurrence_set_digest", analytic_case_occurrence_set_digest + ) + eligibility_digest = _require_digest( + "weight_eligibility_receipt_digest", weight_eligibility_receipt_digest + ) + correction_sequence = _require_positive_integer( + "weight_correction_sequence", weight_correction_sequence + ) + final_digest = _require_digest("final_weight_artifact_digest", final_weight_artifact_digest) + variance_ref = _require_reference( + "variance_design_receipt_reference", + variance_design_receipt_reference, + "variance_design_receipt", + ) + variance_version = _require_positive_integer( + "variance_design_receipt_version", variance_design_receipt_version + ) + variance_digest = _require_digest( + "variance_design_receipt_digest", variance_design_receipt_digest + ) + if variance_digest == point_digest: + raise ValueError( + "variance_design_receipt_digest must identify evidence distinct from the analysis weight receipt." + ) + method_ref = _require_reference( + "variance_method_reference", variance_method_reference, "variance_method" + ) + method_version = _require_positive_integer("variance_method_version", variance_method_version) + evidence_mode = _require_variance_evidence_mode(variance_evidence_mode) + semantics = _require_variance_semantics(variance_semantics) + if evidence_mode == "approximation" and semantics != "approximate": + raise ValueError("approximation evidence must declare approximate variance semantics.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=_restore_operational_uuid("tenant_record_id", tenant_identity), + validity_study_id=_restore_operational_uuid("validity_study_id", study_identity), + sampling_receipt_reference=sampling_ref, + sampling_receipt_version=sampling_version, + sampling_receipt_digest=sampling_digest, + analysis_weight_receipt_digest=point_digest, + analytic_case_occurrence_set_digest=case_digest, + weight_eligibility_receipt_digest=eligibility_digest, + weight_correction_sequence=correction_sequence, + final_weight_artifact_digest=final_digest, + variance_design_receipt_reference=variance_ref, + variance_design_receipt_version=variance_version, + variance_design_receipt_digest=variance_digest, + variance_method_reference=method_ref, + variance_method_version=method_version, + variance_evidence_mode=evidence_mode, + variance_semantics=semantics, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + ) + if persisted is None: + raise WeightVarianceAuthorityNotFound(str(study_id)) + if type(persisted) is not WeightVarianceAuthorityRecord: + raise WeightVarianceAuthorityIntegrityError( + "owner port returned non-canonical point-weight/variance authority evidence" + ) + + try: + record = WeightVarianceAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + authority_reference=persisted.authority_reference, + sampling_receipt_reference=persisted.sampling_receipt_reference, + sampling_receipt_version=persisted.sampling_receipt_version, + sampling_receipt_digest=persisted.sampling_receipt_digest, + analysis_weight_receipt_digest=persisted.analysis_weight_receipt_digest, + analytic_case_occurrence_set_digest=persisted.analytic_case_occurrence_set_digest, + weight_eligibility_receipt_digest=persisted.weight_eligibility_receipt_digest, + weight_correction_sequence=persisted.weight_correction_sequence, + final_weight_artifact_digest=persisted.final_weight_artifact_digest, + variance_design_receipt_reference=persisted.variance_design_receipt_reference, + variance_design_receipt_version=persisted.variance_design_receipt_version, + variance_design_receipt_digest=persisted.variance_design_receipt_digest, + variance_method_reference=persisted.variance_method_reference, + variance_method_version=persisted.variance_method_version, + variance_evidence_mode=persisted.variance_evidence_mode, + variance_semantics=persisted.variance_semantics, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise WeightVarianceAuthorityIntegrityError( + "owner port returned malformed point-weight/variance authority evidence" + ) from exc + if record != persisted: + raise WeightVarianceAuthorityIntegrityError( + "owner port returned non-canonical point-weight/variance authority evidence" + ) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != tenant_identity + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != study_identity + or record.sampling_receipt_reference != sampling_ref + or record.sampling_receipt_version != sampling_version + or record.sampling_receipt_digest != sampling_digest + or record.analysis_weight_receipt_digest != point_digest + or record.analytic_case_occurrence_set_digest != case_digest + or record.weight_eligibility_receipt_digest != eligibility_digest + or record.weight_correction_sequence != correction_sequence + or record.final_weight_artifact_digest != final_digest + or record.variance_design_receipt_reference != variance_ref + or record.variance_design_receipt_version != variance_version + or record.variance_design_receipt_digest != variance_digest + or record.variance_method_reference != method_ref + or record.variance_method_version != method_version + or record.variance_evidence_mode != evidence_mode + or record.variance_semantics != semantics + or record.owner_contract_reference != owner_ref + or record.owner_contract_version != owner_version + ): + raise WeightVarianceAuthorityIntegrityError( + "owner evidence does not match the requested point-weight/variance compatibility" + ) + if use_instant < record.released_at: + raise WeightVarianceAuthorityIntegrityError( + "weight/variance authority cannot be used before its owner-resolved release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise WeightVarianceAuthorityIntegrityError( + "weight/variance authority cannot be used at or after owner-resolved supersession" + ) + + values = { + "authority_reference": record.authority_reference, + "sampling_receipt_reference": record.sampling_receipt_reference, + "sampling_receipt_version": record.sampling_receipt_version, + "sampling_receipt_digest": record.sampling_receipt_digest, + "analysis_weight_receipt_digest": record.analysis_weight_receipt_digest, + "analytic_case_occurrence_set_digest": record.analytic_case_occurrence_set_digest, + "weight_eligibility_receipt_digest": record.weight_eligibility_receipt_digest, + "weight_correction_sequence": record.weight_correction_sequence, + "final_weight_artifact_digest": record.final_weight_artifact_digest, + "variance_design_receipt_reference": record.variance_design_receipt_reference, + "variance_design_receipt_version": record.variance_design_receipt_version, + "variance_design_receipt_digest": record.variance_design_receipt_digest, + "variance_method_reference": record.variance_method_reference, + "variance_method_version": record.variance_method_version, + "variance_evidence_mode": record.variance_evidence_mode, + "variance_semantics": record.variance_semantics, + "owner_contract_reference": record.owner_contract_reference, + "owner_contract_version": record.owner_contract_version, + "owner_contract_digest": record.owner_contract_digest, + "owner_contract_released_at": record.owner_contract_released_at, + "released_at": record.released_at, + "superseded_at": record.superseded_at, + } + fields = tuple((field_name, values[field_name]) for field_name in sorted(_READ_FIELDS)) + return tenant_identity, study_identity, fields + + +def _build_weight_variance_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: WeightVarianceAuthorityView) -> None: + """Verify one weight/variance view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise WeightVarianceAuthorityIntegrityError( + "weight variance view was not issued by resolve_weight_variance_authority" + ) from exc + if marker is not issuance_marker: + raise WeightVarianceAuthorityIntegrityError( + "weight variance view was not issued by resolve_weight_variance_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + sampling_receipt_reference: str, + sampling_receipt_version: int, + sampling_receipt_digest: str, + analysis_weight_receipt_digest: str, + analytic_case_occurrence_set_digest: str, + weight_eligibility_receipt_digest: str, + weight_correction_sequence: int, + final_weight_artifact_digest: str, + variance_design_receipt_reference: str, + variance_design_receipt_version: int, + variance_design_receipt_digest: str, + variance_method_reference: str, + variance_method_version: int, + variance_evidence_mode: str, + variance_semantics: str, + owner_contract_reference: str, + owner_contract_version: int, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightVarianceAuthorityReadPort, + ) -> WeightVarianceAuthorityView: + """Authorize then corroborate one released point/variance compatibility tuple.""" + tenant_identity, study_identity, fields = _resolve_weight_variance_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + sampling_receipt_reference=sampling_receipt_reference, + sampling_receipt_version=sampling_receipt_version, + sampling_receipt_digest=sampling_receipt_digest, + analysis_weight_receipt_digest=analysis_weight_receipt_digest, + analytic_case_occurrence_set_digest=analytic_case_occurrence_set_digest, + weight_eligibility_receipt_digest=weight_eligibility_receipt_digest, + weight_correction_sequence=weight_correction_sequence, + final_weight_artifact_digest=final_weight_artifact_digest, + variance_design_receipt_reference=variance_design_receipt_reference, + variance_design_receipt_version=variance_design_receipt_version, + variance_design_receipt_digest=variance_design_receipt_digest, + variance_method_reference=variance_method_reference, + variance_method_version=variance_method_version, + variance_evidence_mode=variance_evidence_mode, + variance_semantics=variance_semantics, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + view = object.__new__(WeightVarianceAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_weight_variance_view_issued, + resolve_weight_variance_authority, +) = _build_weight_variance_view_runtime() +del _build_weight_variance_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_eligibility_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_eligibility_authority.py new file mode 100644 index 000000000..fec54f29a --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_eligibility_authority.py @@ -0,0 +1,634 @@ +"""Corroborate released cross-sectional/longitudinal weight eligibility. + +The owner port binds one point-weight artifact to its governed population, +reference duration, eligible case set, and scope. It does not copy row-level +weights, person attributes, or foreign application data. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "weight_eligibility_authority" +_OPERATION = "read" +_WEIGHT_SCOPE_CODES = frozenset({"cross_sectional", "longitudinal"}) +_READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "weight_scope_code", + "target_population_reference", + "target_population_digest", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class WeightEligibilityAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the eligibility receipt.""" + + +class WeightEligibilityAuthorityIntegrityError(RuntimeError): + """Indicate that owner evidence cannot corroborate the requested eligibility tuple.""" + + +def _require_weight_scope(value: object) -> str: + """Require explicit cross-sectional or longitudinal eligibility semantics.""" + if type(value) is not str or value not in _WEIGHT_SCOPE_CODES: + raise ValueError("weight_scope_code must be cross_sectional or longitudinal.") + return value + + +class WeightEligibilityAuthorityRecord(tuple): + """Immutable owner projection for one released weight-eligibility receipt.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + weight_scope_code: str, + target_population_reference: str, + target_population_digest: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + ) -> WeightEligibilityAuthorityRecord: + """Validate and detach the minimum immutable eligibility authority.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "eligibility_receipt_reference", + eligibility_receipt_reference, + "weight_eligibility_receipt", + ) + receipt_digest = _require_digest( + "eligibility_receipt_digest", eligibility_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + scope = _require_weight_scope(weight_scope_code) + target_ref = _require_reference( + "target_population_reference", + target_population_reference, + "analysis_target_population", + ) + target_digest = _require_digest( + "target_population_digest", target_population_digest + ) + duration_ref = _require_reference( + "reference_duration_reference", + reference_duration_reference, + "analysis_reference_duration", + ) + duration_digest = _require_digest( + "reference_duration_digest", reference_duration_digest + ) + case_digest = _require_digest( + "eligible_case_set_digest", eligible_case_set_digest + ) + artifact_digest = _require_digest( + "weight_artifact_digest", weight_artifact_digest + ) + constructed = _require_aware_datetime("constructed_at", constructed_at) + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release_instant = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + supersession_instant = ( + None + if superseded_at is None + else _require_aware_datetime("superseded_at", superseded_at) + ) + if release_instant < constructed: + raise ValueError("released_at cannot precede constructed_at.") + if owner_release_instant > release_instant: + raise ValueError( + "owner contract must be released no later than the weight-eligibility authority" + ) + if supersession_instant is not None and supersession_instant <= release_instant: + raise ValueError("superseded_at must be later than released_at.") + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + receipt_ref, + receipt_digest, + version, + scope, + target_ref, + target_digest, + duration_ref, + duration_digest, + case_digest, + artifact_digest, + constructed, + owner_ref, + owner_version, + owner_digest, + release_instant, + owner_release_instant, + supersession_instant, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity for this released evidence.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity for this released evidence.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def eligibility_receipt_reference(self) -> str: + """Return the typed weight-eligibility receipt reference.""" + return self[2] + + @property + def eligibility_receipt_digest(self) -> str: + """Return the exact eligibility receipt digest.""" + return self[3] + + @property + def evidence_version(self) -> int: + """Return the eligibility receipt evidence version.""" + return self[4] + + @property + def weight_scope_code(self) -> str: + """Return cross-sectional or longitudinal eligibility semantics.""" + return self[5] + + @property + def target_population_reference(self) -> str: + """Return the governed analysis target-population reference.""" + return self[6] + + @property + def target_population_digest(self) -> str: + """Return the target-population evidence digest.""" + return self[7] + + @property + def reference_duration_reference(self) -> str: + """Return the governed analysis reference-duration reference.""" + return self[8] + + @property + def reference_duration_digest(self) -> str: + """Return the reference-duration evidence digest.""" + return self[9] + + @property + def eligible_case_set_digest(self) -> str: + """Return the exact eligible-case set digest.""" + return self[10] + + @property + def weight_artifact_digest(self) -> str: + """Return the point-weight artifact governed by this eligibility receipt.""" + return self[11] + + @property + def constructed_at(self) -> datetime: + """Return when the typed eligibility receipt was constructed.""" + return self[12] + + @property + def owner_contract_reference(self) -> str: + """Return the released owner-contract reference.""" + return self[13] + + @property + def owner_contract_version(self) -> int: + """Return the released owner-contract version.""" + return self[14] + + @property + def owner_contract_digest(self) -> str: + """Return the released owner-contract digest.""" + return self[15] + + @property + def released_at(self) -> datetime: + """Return when this eligibility evidence became released authority.""" + return self[16] + + @property + def owner_contract_released_at(self) -> datetime: + """Return when the governing owner contract became released authority.""" + return self[17] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive owner-resolved cutover when this authority is superseded.""" + return self[18] + + +class WeightEligibilityAuthorityView: + """Field-minimized eligibility evidence issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> WeightEligibilityAuthorityView: + """Reject direct construction; only the resolver may issue this view.""" + raise TypeError( + "WeightEligibilityAuthorityView is issued only by " + "resolve_weight_eligibility_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Reject mutation after resolver-controlled issuance.""" + raise AttributeError("WeightEligibilityAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Reject deletion after resolver-controlled issuance.""" + raise AttributeError("WeightEligibilityAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Require the exact in-process marker written by the resolver.""" + _require_weight_eligibility_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable eligibility provenance without row-level values.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class WeightEligibilityAuthorityReadPort(Protocol): + """Owner read contract for released typed weight-eligibility evidence.""" + + def read_weight_eligibility_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + weight_scope_code: str, + target_population_reference: str, + target_population_digest: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> WeightEligibilityAuthorityRecord | None: + """Return matching released eligibility evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + WeightEligibilityAuthorityReadPort, "read_weight_eligibility_authority" +) + + +def _resolve_weight_eligibility_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + weight_scope_code: str, + target_population_reference: str, + target_population_digest: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightEligibilityAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and corroborate released eligibility into inert projection state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static(type(read_port), "read_weight_eligibility_authority", None) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable read_weight_eligibility_authority." + ) + + requested = WeightEligibilityAuthorityRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + eligibility_receipt_reference=eligibility_receipt_reference, + eligibility_receipt_digest=eligibility_receipt_digest, + evidence_version=evidence_version, + weight_scope_code=weight_scope_code, + target_population_reference=target_population_reference, + target_population_digest=target_population_digest, + reference_duration_reference=reference_duration_reference, + reference_duration_digest=reference_duration_digest, + eligible_case_set_digest=eligible_case_set_digest, + weight_artifact_digest=weight_artifact_digest, + constructed_at=constructed_at, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + owner_contract_released_at=constructed_at, + released_at=constructed_at, + superseded_at=None, + ) + tenant_id = requested.tenant_record_id + study_id = requested.validity_study_id + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=requested.tenant_record_id, + validity_study_id=requested.validity_study_id, + eligibility_receipt_reference=requested.eligibility_receipt_reference, + eligibility_receipt_digest=requested.eligibility_receipt_digest, + evidence_version=requested.evidence_version, + weight_scope_code=requested.weight_scope_code, + target_population_reference=requested.target_population_reference, + target_population_digest=requested.target_population_digest, + reference_duration_reference=requested.reference_duration_reference, + reference_duration_digest=requested.reference_duration_digest, + eligible_case_set_digest=requested.eligible_case_set_digest, + weight_artifact_digest=requested.weight_artifact_digest, + constructed_at=requested.constructed_at, + owner_contract_reference=requested.owner_contract_reference, + owner_contract_version=requested.owner_contract_version, + owner_contract_digest=requested.owner_contract_digest, + ) + if persisted is None: + raise WeightEligibilityAuthorityNotFound(str(study_id)) + if type(persisted) is not WeightEligibilityAuthorityRecord: + raise WeightEligibilityAuthorityIntegrityError( + "owner port returned non-canonical weight-eligibility authority evidence" + ) + + try: + record = WeightEligibilityAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + eligibility_receipt_reference=persisted.eligibility_receipt_reference, + eligibility_receipt_digest=persisted.eligibility_receipt_digest, + evidence_version=persisted.evidence_version, + weight_scope_code=persisted.weight_scope_code, + target_population_reference=persisted.target_population_reference, + target_population_digest=persisted.target_population_digest, + reference_duration_reference=persisted.reference_duration_reference, + reference_duration_digest=persisted.reference_duration_digest, + eligible_case_set_digest=persisted.eligible_case_set_digest, + weight_artifact_digest=persisted.weight_artifact_digest, + constructed_at=persisted.constructed_at, + owner_contract_reference=persisted.owner_contract_reference, + owner_contract_version=persisted.owner_contract_version, + owner_contract_digest=persisted.owner_contract_digest, + owner_contract_released_at=persisted.owner_contract_released_at, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise WeightEligibilityAuthorityIntegrityError( + "owner port returned structurally invalid weight-eligibility authority evidence" + ) from exc + if record != persisted: + raise WeightEligibilityAuthorityIntegrityError( + "owner port returned non-canonical weight-eligibility authority structure" + ) + if record[:-3] != requested[:-3]: + raise WeightEligibilityAuthorityIntegrityError( + "released weight-eligibility authority does not match requested coordinates" + ) + if record.released_at > use_instant: + raise WeightEligibilityAuthorityIntegrityError( + "weight-eligibility evidence must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise WeightEligibilityAuthorityIntegrityError( + "weight-eligibility evidence is superseded for this scientific-use instant" + ) + + fields: tuple[tuple[str, object], ...] = ( + ("constructed_at", record.constructed_at), + ("eligibility_receipt_digest", record.eligibility_receipt_digest), + ("eligibility_receipt_reference", record.eligibility_receipt_reference), + ("eligible_case_set_digest", record.eligible_case_set_digest), + ("evidence_version", record.evidence_version), + ("owner_contract_digest", record.owner_contract_digest), + ("owner_contract_reference", record.owner_contract_reference), + ("owner_contract_released_at", record.owner_contract_released_at), + ("owner_contract_version", record.owner_contract_version), + ("reference_duration_digest", record.reference_duration_digest), + ("reference_duration_reference", record.reference_duration_reference), + ("released_at", record.released_at), + ("superseded_at", record.superseded_at), + ("target_population_digest", record.target_population_digest), + ("target_population_reference", record.target_population_reference), + ("weight_artifact_digest", record.weight_artifact_digest), + ("weight_scope_code", record.weight_scope_code), + ) + return ( + _store_operational_uuid("tenant_record_id", tenant_id), + _store_operational_uuid("validity_study_id", study_id), + fields, + ) + + +def _build_weight_eligibility_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: WeightEligibilityAuthorityView) -> None: + """Verify one eligibility view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise WeightEligibilityAuthorityIntegrityError( + "weight-eligibility authority view was not issued by the resolver" + ) from exc + if marker is not issuance_marker: + raise WeightEligibilityAuthorityIntegrityError( + "weight-eligibility authority view was not issued by the resolver" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + weight_scope_code: str, + target_population_reference: str, + target_population_digest: str, + reference_duration_reference: str, + reference_duration_digest: str, + eligible_case_set_digest: str, + weight_artifact_digest: str, + constructed_at: datetime, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightEligibilityAuthorityReadPort, + ) -> WeightEligibilityAuthorityView: + """Authorize then corroborate exact released weight-eligibility evidence.""" + tenant_identity, study_identity, fields = _resolve_weight_eligibility_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + eligibility_receipt_reference=eligibility_receipt_reference, + eligibility_receipt_digest=eligibility_receipt_digest, + evidence_version=evidence_version, + weight_scope_code=weight_scope_code, + target_population_reference=target_population_reference, + target_population_digest=target_population_digest, + reference_duration_reference=reference_duration_reference, + reference_duration_digest=reference_duration_digest, + eligible_case_set_digest=eligible_case_set_digest, + weight_artifact_digest=weight_artifact_digest, + constructed_at=constructed_at, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + view = object.__new__(WeightEligibilityAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_weight_eligibility_view_issued, + resolve_weight_eligibility_authority, +) = _build_weight_eligibility_view_runtime() +del _build_weight_eligibility_view_runtime diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_eligibility_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_eligibility_supersession_authority.py new file mode 100644 index 000000000..56a139fca --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_eligibility_supersession_authority.py @@ -0,0 +1,573 @@ +"""Corroborate append-only weight-eligibility correction authority. + +The ordinary eligibility projection proves which population, reference duration, +case set, and point-weight artifact one receipt governs. This boundary proves +when that released receipt remained authoritative and which released successor +ended its half-open authority interval. It keeps row-level weights and person +attributes behind their scientific owners. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "weight_eligibility_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_eligibility_receipt_reference", + "successor_eligibility_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) +_VIEW_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class WeightEligibilitySupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the eligibility receipt.""" + + +class WeightEligibilitySupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released eligibility correction evidence cannot authorize use.""" + + +class WeightEligibilitySupersessionAuthorityRecord(tuple): + """Immutable owner projection for one eligibility receipt authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_eligibility_receipt_reference: str | None = None, + successor_eligibility_receipt_digest: str | None = None, + successor_evidence_version: int | None = None, + successor_released_at: datetime | None = None, + ) -> WeightEligibilitySupersessionAuthorityRecord: + """Validate one released predecessor and its optional atomic successor edge.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + receipt_ref = _require_reference( + "eligibility_receipt_reference", + eligibility_receipt_reference, + "weight_eligibility_receipt", + ) + receipt_digest = _require_digest( + "eligibility_receipt_digest", eligibility_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release > release_instant: + raise ValueError( + "owner contract must be released no later than weight-eligibility receipt." + ) + + successor_values = ( + superseded_at, + successor_eligibility_receipt_reference, + successor_eligibility_receipt_digest, + successor_evidence_version, + successor_released_at, + ) + if all(value is None for value in successor_values): + cutover = None + successor_ref = None + successor_digest = None + successor_version = None + successor_release = None + elif any(value is None for value in successor_values): + raise ValueError( + "eligibility supersession requires cutover and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_eligibility_receipt_reference", + successor_eligibility_receipt_reference, + "weight_eligibility_receipt", + ) + successor_digest = _require_digest( + "successor_eligibility_receipt_digest", + successor_eligibility_receipt_digest, + ) + successor_version = _require_positive_integer( + "successor_evidence_version", successor_evidence_version + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover <= release_instant: + raise ValueError("superseded_at must be later than eligibility receipt release.") + if successor_ref == receipt_ref: + raise ValueError("successor eligibility receipt must have a new reference.") + if successor_digest == receipt_digest: + raise ValueError("successor eligibility receipt must identify new evidence.") + if successor_version != 1: + raise ValueError("successor_evidence_version must remain 1.") + if successor_release <= release_instant: + raise ValueError( + "successor eligibility receipt must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor eligibility receipt must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("eligibility_receipt_digest", receipt_digest), + ("eligibility_receipt_reference", receipt_ref), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", owner_release), + ("owner_contract_version", owner_version), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_eligibility_receipt_digest", successor_digest), + ("successor_eligibility_receipt_reference", successor_ref), + ("successor_evidence_version", successor_version), + ("successor_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-receipt authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this eligibility receipt became released authority.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this receipt's authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class WeightEligibilitySupersessionAuthorityView: + """Sealed current-receipt authority issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> WeightEligibilitySupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "WeightEligibilitySupersessionAuthorityView is issued only by " + "resolve_weight_eligibility_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("WeightEligibilitySupersessionAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("WeightEligibilitySupersessionAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_weight_eligibility_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return current eligibility authority without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class WeightEligibilitySupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released eligibility correction state.""" + + def read_weight_eligibility_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> WeightEligibilitySupersessionAuthorityRecord | None: + """Return matching released eligibility supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + WeightEligibilitySupersessionAuthorityReadPort, + "read_weight_eligibility_supersession_authority", +) + + +def _resolve_weight_eligibility_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightEligibilitySupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve eligibility supersession into inert view state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_weight_eligibility_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable " + "read_weight_eligibility_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + receipt_ref = _require_reference( + "eligibility_receipt_reference", + eligibility_receipt_reference, + "weight_eligibility_receipt", + ) + receipt_digest = _require_digest( + "eligibility_receipt_digest", eligibility_receipt_digest + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + eligibility_receipt_reference=receipt_ref, + eligibility_receipt_digest=receipt_digest, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise WeightEligibilitySupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not WeightEligibilitySupersessionAuthorityRecord: + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "owner port returned non-canonical weight-eligibility supersession evidence" + ) + + try: + successor_values = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = WeightEligibilitySupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_eligibility_receipt_reference=( + None + if successor_values is None + else successor_values["successor_eligibility_receipt_reference"] + ), + successor_eligibility_receipt_digest=( + None + if successor_values is None + else successor_values["successor_eligibility_receipt_digest"] + ), + successor_evidence_version=( + None + if successor_values is None + else successor_values["successor_evidence_version"] + ), + successor_released_at=( + None if successor_values is None else successor_values["successor_released_at"] + ), + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "owner port returned structurally invalid weight-eligibility supersession evidence" + ) from exc + if record != persisted: + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "owner port returned non-canonical weight-eligibility supersession structure" + ) + + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["eligibility_receipt_reference"] != receipt_ref + or record_values["eligibility_receipt_digest"] != receipt_digest + or record_values["evidence_version"] != version + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "released eligibility supersession authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "weight-eligibility authority cannot be used before its release instant" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "weight-eligibility authority cannot be used at or after supersession" + ) + + values = dict(record.fields) + values["released_at"] = record.released_at + fields = tuple((field_name, values[field_name]) for field_name in sorted(_VIEW_FIELDS)) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_weight_eligibility_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: WeightEligibilitySupersessionAuthorityView) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "weight-eligibility supersession view was not issued by " + "resolve_weight_eligibility_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise WeightEligibilitySupersessionAuthorityIntegrityError( + "weight-eligibility supersession view was not issued by " + "resolve_weight_eligibility_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightEligibilitySupersessionAuthorityReadPort, + ) -> WeightEligibilitySupersessionAuthorityView: + """Authorize then resolve the eligibility receipt authority interval.""" + tenant_identity, study_identity, fields = ( + _resolve_weight_eligibility_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + eligibility_receipt_reference=eligibility_receipt_reference, + eligibility_receipt_digest=eligibility_receipt_digest, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(WeightEligibilitySupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_weight_eligibility_supersession_view_issued, + resolve_weight_eligibility_supersession_authority, +) = _build_weight_eligibility_supersession_view_runtime() +del _build_weight_eligibility_supersession_view_runtime + + +__all__ = [ + "WeightEligibilitySupersessionAuthorityIntegrityError", + "WeightEligibilitySupersessionAuthorityNotFound", + "WeightEligibilitySupersessionAuthorityReadPort", + "WeightEligibilitySupersessionAuthorityRecord", + "WeightEligibilitySupersessionAuthorityView", + "resolve_weight_eligibility_supersession_authority", +] diff --git a/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_variance_supersession_authority.py b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_variance_supersession_authority.py new file mode 100644 index 000000000..80b714cf5 --- /dev/null +++ b/services/workforce-validation-api/src/orgmetra_workforce_validation_api/weight_variance_supersession_authority.py @@ -0,0 +1,528 @@ +"""Corroborate append-only point-weight/variance compatibility corrections. + +The ordinary compatibility projection proves which released sampling, point-weight, +and variance-design coordinates were used together. This boundary proves the +half-open authority interval for that immutable compatibility identity and, when +corrected, the exact released successor identity that ends the interval. +Successor chronology is owner-resolved rather than caller-selected. +""" + +from __future__ import annotations + +from datetime import datetime +from inspect import getattr_static +from types import FunctionType +from typing import Protocol, runtime_checkable +from uuid import UUID + +from orgmetra_keyverse_adapter import ( + PurposeBoundAccessPolicy, + PurposeBoundAccessRequest, + require_purpose_bound_access, +) + +from .registry import ( + ValidationPrincipal, + _detach_policy, + _require_aware_datetime, + _require_code, + _restore_operational_uuid, + _store_operational_uuid, +) +from .scientific_authority import ( + _require_digest, + _require_positive_integer, + _require_reference, +) + +_RESOURCE_KIND = "weight_variance_supersession_authority" +_OPERATION = "read" +_READ_FIELDS = frozenset( + { + "authority_reference", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_authority_reference", + "successor_evidence_version", + "successor_released_at", + } +) + + +class WeightVarianceSupersessionAuthorityNotFound(LookupError): + """Indicate that no released owner evidence corroborates the compatibility authority.""" + + +class WeightVarianceSupersessionAuthorityIntegrityError(RuntimeError): + """Indicate that released compatibility correction evidence cannot authorize use.""" + + +class WeightVarianceSupersessionAuthorityRecord(tuple): + """Immutable owner projection for one compatibility-authority interval.""" + + __slots__ = () + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + owner_contract_released_at: datetime, + released_at: datetime, + superseded_at: datetime | None = None, + successor_authority_reference: str | None = None, + successor_evidence_version: int | None = None, + successor_released_at: datetime | None = None, + ) -> WeightVarianceSupersessionAuthorityRecord: + """Validate one released predecessor and its optional atomic successor edge.""" + tenant_identity = _store_operational_uuid("tenant_record_id", tenant_record_id) + study_identity = _store_operational_uuid("validity_study_id", validity_study_id) + authority_ref = _require_reference( + "authority_reference", authority_reference, "variance_compatibility_authority" + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer( + "owner_contract_version", owner_contract_version + ) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + owner_release = _require_aware_datetime( + "owner_contract_released_at", owner_contract_released_at + ) + release_instant = _require_aware_datetime("released_at", released_at) + if owner_release > release_instant: + raise ValueError( + "owner contract must be released no later than compatibility authority release." + ) + + successor_values = ( + superseded_at, + successor_authority_reference, + successor_evidence_version, + successor_released_at, + ) + if all(value is None for value in successor_values): + cutover = None + successor_ref = None + successor_version = None + successor_release = None + elif any(value is None for value in successor_values): + raise ValueError( + "weight/variance supersession requires cutover and complete released successor coordinates." + ) + else: + cutover = _require_aware_datetime("superseded_at", superseded_at) + successor_ref = _require_reference( + "successor_authority_reference", + successor_authority_reference, + "variance_compatibility_authority", + ) + successor_version = _require_positive_integer( + "successor_evidence_version", successor_evidence_version + ) + successor_release = _require_aware_datetime( + "successor_released_at", successor_released_at + ) + if cutover <= release_instant: + raise ValueError( + "superseded_at must be later than compatibility authority release." + ) + if successor_ref == authority_ref: + raise ValueError("successor compatibility authority must have a new reference.") + if successor_version != 1: + raise ValueError("successor_evidence_version must remain 1.") + if successor_release <= release_instant: + raise ValueError( + "successor compatibility authority must be released after its predecessor." + ) + if successor_release != cutover: + raise ValueError( + "successor compatibility authority must be released exactly at supersession." + ) + + current_fields: tuple[tuple[str, object], ...] = ( + ("authority_reference", authority_ref), + ("evidence_version", version), + ("owner_contract_digest", owner_digest), + ("owner_contract_reference", owner_ref), + ("owner_contract_released_at", owner_release), + ("owner_contract_version", owner_version), + ) + successor_fields: tuple[tuple[str, object], ...] | None + if cutover is None: + successor_fields = None + else: + successor_fields = ( + ("successor_authority_reference", successor_ref), + ("successor_evidence_version", successor_version), + ("successor_released_at", successor_release), + ) + return tuple.__new__( + cls, + ( + tenant_identity, + study_identity, + current_fields, + release_instant, + cutover, + successor_fields, + ), + ) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh tenant identity.""" + return _restore_operational_uuid("tenant_record_id", self[0]) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh validity-study identity.""" + return _restore_operational_uuid("validity_study_id", self[1]) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return immutable current-authority coordinates.""" + return self[2] + + @property + def released_at(self) -> datetime: + """Return when this compatibility authority became released.""" + return self[3] + + @property + def superseded_at(self) -> datetime | None: + """Return the exclusive end of this authority interval.""" + return self[4] + + @property + def successor_fields(self) -> tuple[tuple[str, object], ...] | None: + """Return internal released successor coordinates, if any.""" + return self[5] + + +class WeightVarianceSupersessionAuthorityView: + """Sealed current compatibility authority issued only after authorization.""" + + __slots__ = ("_tenant_identity", "_study_identity", "_fields", "_issuance_marker") + + def __new__( + cls, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + fields: tuple[tuple[str, object], ...], + ) -> WeightVarianceSupersessionAuthorityView: + """Reject public construction; only the resolver may issue this view.""" + raise TypeError( + "WeightVarianceSupersessionAuthorityView is issued only by " + "resolve_weight_variance_supersession_authority." + ) + + def __setattr__(self, name: str, value: object) -> None: + """Keep ordinary callers from mutating issued projection state.""" + raise AttributeError("WeightVarianceSupersessionAuthorityView is immutable.") + + def __delattr__(self, name: str) -> None: + """Keep ordinary callers from deleting issued projection state.""" + raise AttributeError("WeightVarianceSupersessionAuthorityView is immutable.") + + def _require_issued(self) -> None: + """Reject exact-runtime allocations not sealed by the resolver.""" + _require_weight_variance_supersession_view_issued(self) + + @property + def tenant_record_id(self) -> UUID: + """Return a fresh authorized tenant identity.""" + self._require_issued() + return _restore_operational_uuid( + "tenant_record_id", object.__getattribute__(self, "_tenant_identity") + ) + + @property + def validity_study_id(self) -> UUID: + """Return a fresh authorized validity-study identity.""" + self._require_issued() + return _restore_operational_uuid( + "validity_study_id", object.__getattribute__(self, "_study_identity") + ) + + @property + def fields(self) -> tuple[tuple[str, object], ...]: + """Return current authority without successor disclosure.""" + self._require_issued() + return object.__getattribute__(self, "_fields") + + +@runtime_checkable +class WeightVarianceSupersessionAuthorityReadPort(Protocol): + """Owner read contract for one released compatibility correction state.""" + + def read_weight_variance_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> WeightVarianceSupersessionAuthorityRecord | None: + """Return matching released compatibility supersession evidence or ``None``.""" + ... + + +_PROTOCOL_READ_CAPABILITY = getattr_static( + WeightVarianceSupersessionAuthorityReadPort, + "read_weight_variance_supersession_authority", +) + + +def _resolve_weight_variance_supersession_authority_state( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightVarianceSupersessionAuthorityReadPort, +) -> tuple[int, int, tuple[tuple[str, object], ...]]: + """Authorize and resolve compatibility supersession into inert view state.""" + if type(principal) is not ValidationPrincipal: + raise TypeError("principal must be an exact ValidationPrincipal.") + if type(policy) is not PurposeBoundAccessPolicy: + raise TypeError("policy must be an exact PurposeBoundAccessPolicy.") + read_capability = getattr_static( + type(read_port), "read_weight_variance_supersession_authority", None + ) + if ( + type(read_capability) is not FunctionType + or read_capability is _PROTOCOL_READ_CAPABILITY + ): + raise TypeError( + "read_port must expose a statically callable read_weight_variance_supersession_authority." + ) + + tenant_id = _restore_operational_uuid( + "tenant_record_id", _store_operational_uuid("tenant_record_id", tenant_record_id) + ) + study_id = _restore_operational_uuid( + "validity_study_id", _store_operational_uuid("validity_study_id", validity_study_id) + ) + authority_ref = _require_reference( + "authority_reference", authority_reference, "variance_compatibility_authority" + ) + version = _require_positive_integer("evidence_version", evidence_version) + if version != 1: + raise ValueError("evidence_version must remain 1.") + owner_ref = _require_reference( + "owner_contract_reference", owner_contract_reference, "released_owner_contract" + ) + owner_version = _require_positive_integer("owner_contract_version", owner_contract_version) + owner_digest = _require_digest("owner_contract_digest", owner_contract_digest) + use_instant = _require_aware_datetime("used_at", used_at) + purpose = _require_code("purpose_code", purpose_code) + detached_principal = ValidationPrincipal( + tenant_record_id=principal.tenant_record_id, + actor_reference=principal.actor_reference, + granted_scope_codes=principal.granted_scope_codes, + ) + detached_policy = _detach_policy(policy) + + require_purpose_bound_access( + request=PurposeBoundAccessRequest( + tenant_record_id=tenant_id, + actor_tenant_record_id=detached_principal.tenant_record_id, + resource_tenant_record_id=tenant_id, + actor_reference=detached_principal.actor_reference, + resource_reference=f"{_RESOURCE_KIND}:{study_id}", + purpose_code=purpose, + operation_code=_OPERATION, + resource_kind=_RESOURCE_KIND, + requested_fields=_READ_FIELDS, + granted_scope_codes=detached_principal.granted_scope_codes, + ), + policy=detached_policy, + ) + + persisted = read_capability( + read_port, + tenant_record_id=tenant_id, + validity_study_id=study_id, + authority_reference=authority_ref, + evidence_version=version, + owner_contract_reference=owner_ref, + owner_contract_version=owner_version, + owner_contract_digest=owner_digest, + ) + if persisted is None: + raise WeightVarianceSupersessionAuthorityNotFound(str(study_id)) + if type(persisted) is not WeightVarianceSupersessionAuthorityRecord: + raise WeightVarianceSupersessionAuthorityIntegrityError( + "owner port returned non-canonical weight/variance supersession evidence" + ) + + try: + successor_values = ( + None if persisted.successor_fields is None else dict(persisted.successor_fields) + ) + record = WeightVarianceSupersessionAuthorityRecord( + tenant_record_id=persisted.tenant_record_id, + validity_study_id=persisted.validity_study_id, + released_at=persisted.released_at, + superseded_at=persisted.superseded_at, + successor_authority_reference=( + None + if successor_values is None + else successor_values["successor_authority_reference"] + ), + successor_evidence_version=( + None + if successor_values is None + else successor_values["successor_evidence_version"] + ), + successor_released_at=( + None if successor_values is None else successor_values["successor_released_at"] + ), + **dict(persisted.fields), + ) + except (IndexError, KeyError, TypeError, ValueError) as exc: + raise WeightVarianceSupersessionAuthorityIntegrityError( + "owner port returned structurally invalid weight/variance supersession evidence" + ) from exc + if record != persisted: + raise WeightVarianceSupersessionAuthorityIntegrityError( + "owner port returned non-canonical weight/variance supersession structure" + ) + + record_values = dict(record.fields) + if ( + _store_operational_uuid("record tenant_record_id", record.tenant_record_id) + != _store_operational_uuid("requested tenant_record_id", tenant_id) + or _store_operational_uuid("record validity_study_id", record.validity_study_id) + != _store_operational_uuid("requested validity_study_id", study_id) + or record_values["authority_reference"] != authority_ref + or record_values["evidence_version"] != version + or record_values["owner_contract_reference"] != owner_ref + or record_values["owner_contract_version"] != owner_version + or record_values["owner_contract_digest"] != owner_digest + ): + raise WeightVarianceSupersessionAuthorityIntegrityError( + "released weight/variance supersession authority does not match requested coordinates" + ) + if use_instant < record.released_at: + raise WeightVarianceSupersessionAuthorityIntegrityError( + "compatibility authority must be released before scientific use" + ) + if record.superseded_at is not None and use_instant >= record.superseded_at: + raise WeightVarianceSupersessionAuthorityIntegrityError( + "compatibility authority is superseded for this scientific-use instant" + ) + + fields = record.fields + ( + ("released_at", record.released_at), + ("superseded_at", record.superseded_at), + ) + return ( + _store_operational_uuid("tenant_record_id", record.tenant_record_id), + _store_operational_uuid("validity_study_id", record.validity_study_id), + fields, + ) + + +def _build_weight_variance_supersession_view_runtime(): + """Create closure-private sealing state and the authorized public resolver.""" + issuance_marker = object() + + def require_issued(view: WeightVarianceSupersessionAuthorityView) -> None: + """Verify one supersession view against the closure-private capability.""" + try: + marker = object.__getattribute__(view, "_issuance_marker") + except AttributeError as exc: + raise WeightVarianceSupersessionAuthorityIntegrityError( + "weight/variance supersession view was not issued by " + "resolve_weight_variance_supersession_authority" + ) from exc + if marker is not issuance_marker: + raise WeightVarianceSupersessionAuthorityIntegrityError( + "weight/variance supersession view was not issued by " + "resolve_weight_variance_supersession_authority" + ) + + def resolve( + *, + principal: ValidationPrincipal, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + used_at: datetime, + purpose_code: str, + policy: PurposeBoundAccessPolicy, + read_port: WeightVarianceSupersessionAuthorityReadPort, + ) -> WeightVarianceSupersessionAuthorityView: + """Authorize then resolve the compatibility authority's append-only interval.""" + tenant_identity, study_identity, fields = ( + _resolve_weight_variance_supersession_authority_state( + principal=principal, + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + authority_reference=authority_reference, + evidence_version=evidence_version, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=owner_contract_digest, + used_at=used_at, + purpose_code=purpose_code, + policy=policy, + read_port=read_port, + ) + ) + view = object.__new__(WeightVarianceSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", tenant_identity) + object.__setattr__(view, "_study_identity", study_identity) + object.__setattr__(view, "_fields", fields) + object.__setattr__(view, "_issuance_marker", issuance_marker) + return view + + return require_issued, resolve + + +( + _require_weight_variance_supersession_view_issued, + resolve_weight_variance_supersession_authority, +) = _build_weight_variance_supersession_view_runtime() +del _build_weight_variance_supersession_view_runtime + + +__all__ = [ + "WeightVarianceSupersessionAuthorityIntegrityError", + "WeightVarianceSupersessionAuthorityNotFound", + "WeightVarianceSupersessionAuthorityReadPort", + "WeightVarianceSupersessionAuthorityRecord", + "WeightVarianceSupersessionAuthorityView", + "resolve_weight_variance_supersession_authority", +] diff --git a/services/workforce-validation-api/tests/test_authorized_view_seal_capability.py b/services/workforce-validation-api/tests/test_authorized_view_seal_capability.py new file mode 100644 index 000000000..8a273ac34 --- /dev/null +++ b/services/workforce-validation-api/tests/test_authorized_view_seal_capability.py @@ -0,0 +1,51 @@ +"""Reject module-exposed capabilities that can mint authorized public views.""" + +from __future__ import annotations + +from inspect import getmodule +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api as api +from orgmetra_workforce_validation_api import registry + + +TENANT = UUID("11111111-1111-4111-8111-111111111111") +STUDY = UUID("22222222-2222-4222-8222-222222222222") + + +def test_public_authorized_view_modules_expose_no_issuance_marker_capability() -> None: + """Keep view-sealing write capabilities out of ordinary module state.""" + for name in sorted(item for item in api.__all__ if item.endswith("View")): + view_type = getattr(api, name) + module = getmodule(view_type) + assert module is not None + exposed = tuple( + sorted(key for key in vars(module) if key.endswith("_ISSUANCE_MARKER")) + ) + assert exposed == (), f"{name} owner module exposes issuance markers: {exposed!r}" + + +def test_registry_module_marker_cannot_mint_authorized_projection() -> None: + """Reject a raw view whose caller fills slots with any importable marker-like value.""" + forged = object.__new__(registry.ValidityStudyView) + object.__setattr__(forged, "_tenant_identity", TENANT.int) + object.__setattr__(forged, "_study_identity", STUDY.int) + object.__setattr__( + forged, + "_fields", + (("study_status_code", "study_closed"),), + ) + caller_marker = getattr( + registry, + "_VALIDITY_STUDY_VIEW_ISSUANCE_MARKER", + object(), + ) + object.__setattr__(forged, "_issuance_marker", caller_marker) + + with pytest.raises( + registry.ValidityStudyIntegrityError, + match="was not issued by read_validity_study", + ): + _ = forged.fields diff --git a/services/workforce-validation-api/tests/test_base_weight_authority.py b/services/workforce-validation-api/tests/test_base_weight_authority.py new file mode 100644 index 000000000..7995d08f4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_authority.py @@ -0,0 +1,302 @@ +"""Fail-closed contract for released base/design-weight provenance.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.base_weight_authority import ( + BaseWeightAuthorityIntegrityError, + BaseWeightAuthorityNotFound, + BaseWeightAuthorityReadPort, + BaseWeightAuthorityRecord, + BaseWeightAuthorityView, + resolve_base_weight_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +BASE_RECEIPT_REFERENCE = "base_weight_evidence_receipt:11111111-1111-4111-8111-111111111111" +SOURCE_REFERENCE = "source_universe_receipt:22222222-2222-4222-8222-222222222222" +SAMPLING_REFERENCE = "sampling_design_receipt:33333333-3333-4333-8333-333333333333" +OWNER_REFERENCE = "released_owner_contract:44444444-4444-4444-8444-444444444444" +BASE_RECEIPT_DIGEST = "1" * 64 +SOURCE_DIGEST = "2" * 64 +SAMPLING_DIGEST = "3" * 64 +SAMPLED_SET_DIGEST = "4" * 64 +SELECTION_PROBABILITY_SET_DIGEST = "5" * 64 +BASE_ARTIFACT_DIGEST = "6" * 64 +OWNER_DIGEST = "7" * 64 +SOURCE_RELEASED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +SAMPLING_RELEASED_AT = datetime(2026, 9, 17, 6, 30, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 17, 6, 45, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 7, 30, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "evidence_version", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "source_universe_released_at", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "sampling_design_released_at", + "sampled_occurrence_set_digest", + "selection_probability_set_digest", + "selection_stage_count", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured owner evidence and capture lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_base_weight_authority(self, **coordinates: object) -> object: + self.calls.append(dict(coordinates)) + return self.result + + +class _ProtocolOnly(BaseWeightAuthorityReadPort): + """Inherit only the Protocol placeholder.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must never execute.""" + + @property + def read_base_weight_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +class _NoReadMethod: + """Deliberately omit the owner capability.""" + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="base-weight-authority-read-v1", + resource_kind="base_weight_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> BaseWeightAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "base_weight_evidence_receipt_reference": BASE_RECEIPT_REFERENCE, + "base_weight_evidence_receipt_digest": BASE_RECEIPT_DIGEST, + "evidence_version": 1, + "source_universe_receipt_reference": SOURCE_REFERENCE, + "source_universe_receipt_version": 4, + "source_universe_receipt_digest": SOURCE_DIGEST, + "source_universe_released_at": SOURCE_RELEASED_AT, + "sampling_design_receipt_reference": SAMPLING_REFERENCE, + "sampling_design_receipt_version": 3, + "sampling_design_receipt_digest": SAMPLING_DIGEST, + "sampling_design_released_at": SAMPLING_RELEASED_AT, + "sampled_occurrence_set_digest": SAMPLED_SET_DIGEST, + "selection_probability_set_digest": SELECTION_PROBABILITY_SET_DIGEST, + "selection_stage_count": 2, + "base_weight_method_code": "inverse_inclusion_probability", + "base_weight_method_version": 1, + "base_weight_artifact_digest": BASE_ARTIFACT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 6, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return BaseWeightAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> BaseWeightAuthorityView: + values = dict(_record().fields) + for owner_resolved_field in ( + "source_universe_released_at", + "sampling_design_released_at", + "owner_contract_released_at", + ): + values.pop(owner_resolved_field) + values.update( + { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + ) + values.update(overrides) + return resolve_base_weight_authority(**values) + + +def test_resolution_binds_sampling_stage_probabilities_to_base_weight_artifact() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, BaseWeightAuthorityReadPort) + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert len(port.calls) == 1 + assert port.calls[0]["source_universe_receipt_version"] == 4 + assert port.calls[0]["sampling_design_receipt_version"] == 3 + assert port.calls[0]["selection_probability_set_digest"] == SELECTION_PROBABILITY_SET_DIGEST + assert "source_universe_released_at" not in port.calls[0] + assert "sampling_design_released_at" not in port.calls[0] + assert "owner_contract_released_at" not in port.calls[0] + assert ("sampled_occurrence_set_digest", SAMPLED_SET_DIGEST) in view.fields + assert ("selection_stage_count", 2) in view.fields + assert ("base_weight_artifact_digest", BASE_ARTIFACT_DIGEST) in view.fields + assert ("source_universe_released_at", SOURCE_RELEASED_AT) in view.fields + assert ("sampling_design_released_at", SAMPLING_RELEASED_AT) in view.fields + assert ("owner_contract_released_at", OWNER_CONTRACT_RELEASED_AT) in view.fields + assert ("released_at", RELEASED_AT) in view.fields + + +def test_prerequisite_releases_are_resolved_from_owner_evidence() -> None: + source_release = SOURCE_RELEASED_AT + timedelta(seconds=1) + sampling_release = SAMPLING_RELEASED_AT + timedelta(seconds=1) + owner_release = OWNER_CONTRACT_RELEASED_AT + timedelta(seconds=1) + view = _resolve( + read_port=_ReadPort( + _record( + source_universe_released_at=source_release, + sampling_design_released_at=sampling_release, + owner_contract_released_at=owner_release, + ) + ) + ) + + assert ("source_universe_released_at", source_release) in view.fields + assert ("sampling_design_released_at", sampling_release) in view.fields + assert ("owner_contract_released_at", owner_release) in view.fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_noncanonical_or_mismatched_owner_evidence_fails_closed() -> None: + with pytest.raises(BaseWeightAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(selection_stage_count=3))) + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(base_weight_artifact_digest="8" * 64))) + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(tenant_record_id=OTHER_TENANT))) + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(validity_study_id=OTHER_STUDY))) + + +def test_source_sampling_and_release_chronology_fail_closed() -> None: + with pytest.raises(ValueError): + _record(source_universe_released_at=CONSTRUCTED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record(sampling_design_released_at=CONSTRUCTED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record(released_at=CONSTRUCTED_AT - timedelta(seconds=1)) + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(seconds=1)) + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("base_weight_evidence_receipt_reference", "wrong:receipt", ValueError), + ("base_weight_evidence_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("evidence_version", 2, ValueError), + ("source_universe_receipt_reference", "wrong:source", ValueError), + ("source_universe_receipt_version", 0, ValueError), + ("source_universe_receipt_digest", "2" * 63, ValueError), + ("sampling_design_receipt_reference", "wrong:sampling", ValueError), + ("sampling_design_receipt_version", False, ValueError), + ("sampling_design_receipt_digest", "3" * 63, ValueError), + ("sampled_occurrence_set_digest", "4" * 63, ValueError), + ("selection_probability_set_digest", "5" * 63, ValueError), + ("selection_stage_count", 0, ValueError), + ("base_weight_method_code", "Inverse Probability", ValueError), + ("base_weight_method_version", False, ValueError), + ("base_weight_artifact_digest", "6" * 63, ValueError), + ("owner_contract_reference", "wrong:owner", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "7" * 63, ValueError), + ], +) +def test_hostile_coordinates_fail_closed(key: str, value: object, error: type[Exception]) -> None: + with pytest.raises(error): + _resolve(read_port=_ReadPort(_record()), **{key: value}) + + +def test_view_cannot_be_constructed_directly() -> None: + with pytest.raises(TypeError): + BaseWeightAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +@pytest.mark.parametrize("read_port", [_NoReadMethod(), _ProtocolOnly(), _DescriptorReadPort()]) +def test_nonconcrete_owner_capabilities_fail_before_resolution(read_port: object) -> None: + """Reject absent, protocol-only, and descriptor owner capabilities statically.""" + with pytest.raises(TypeError, match="statically callable"): + _resolve(read_port=read_port) diff --git a/services/workforce-validation-api/tests/test_base_weight_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_base_weight_authority_structural_integrity.py new file mode 100644 index 000000000..cbf5fcf81 --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_authority_structural_integrity.py @@ -0,0 +1,173 @@ +"""Regression coverage for canonical base-weight owner evidence structure.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.base_weight_authority import ( + BaseWeightAuthorityIntegrityError, + BaseWeightAuthorityRecord, + resolve_base_weight_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +BASE_RECEIPT_REFERENCE = "base_weight_evidence_receipt:11111111-1111-4111-8111-111111111111" +SOURCE_REFERENCE = "source_universe_receipt:22222222-2222-4222-8222-222222222222" +SAMPLING_REFERENCE = "sampling_design_receipt:33333333-3333-4333-8333-333333333333" +OWNER_REFERENCE = "released_owner_contract:44444444-4444-4444-8444-444444444444" +BASE_RECEIPT_DIGEST = "1" * 64 +SOURCE_DIGEST = "2" * 64 +SAMPLING_DIGEST = "3" * 64 +SAMPLED_SET_DIGEST = "4" * 64 +SELECTION_PROBABILITY_SET_DIGEST = "5" * 64 +BASE_ARTIFACT_DIGEST = "6" * 64 +OWNER_DIGEST = "7" * 64 +SOURCE_RELEASED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +SAMPLING_RELEASED_AT = datetime(2026, 9, 17, 6, 30, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 17, 6, 45, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 7, 30, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "evidence_version", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "source_universe_released_at", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "sampling_design_released_at", + "sampled_occurrence_set_digest", + "selection_probability_set_digest", + "selection_stage_count", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured owner evidence without normalizing its tuple structure.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_base_weight_authority(self, **_: object) -> object: + """Return the configured raw owner result.""" + return self.result + + +def _record() -> BaseWeightAuthorityRecord: + """Build one valid canonical base-weight authority record.""" + return BaseWeightAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + base_weight_evidence_receipt_reference=BASE_RECEIPT_REFERENCE, + base_weight_evidence_receipt_digest=BASE_RECEIPT_DIGEST, + evidence_version=1, + source_universe_receipt_reference=SOURCE_REFERENCE, + source_universe_receipt_version=4, + source_universe_receipt_digest=SOURCE_DIGEST, + source_universe_released_at=SOURCE_RELEASED_AT, + sampling_design_receipt_reference=SAMPLING_REFERENCE, + sampling_design_receipt_version=3, + sampling_design_receipt_digest=SAMPLING_DIGEST, + sampling_design_released_at=SAMPLING_RELEASED_AT, + sampled_occurrence_set_digest=SAMPLED_SET_DIGEST, + selection_probability_set_digest=SELECTION_PROBABILITY_SET_DIGEST, + selection_stage_count=2, + base_weight_method_code="inverse_inclusion_probability", + base_weight_method_version=1, + base_weight_artifact_digest=BASE_ARTIFACT_DIGEST, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=6, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def _resolve(read_port: object) -> object: + """Resolve the canonical coordinates through a supplied raw owner port.""" + principal = ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="base-weight-authority-read-v1", + resource_kind="base_weight_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + return resolve_base_weight_authority( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + base_weight_evidence_receipt_reference=BASE_RECEIPT_REFERENCE, + base_weight_evidence_receipt_digest=BASE_RECEIPT_DIGEST, + evidence_version=1, + source_universe_receipt_reference=SOURCE_REFERENCE, + source_universe_receipt_version=4, + source_universe_receipt_digest=SOURCE_DIGEST, + sampling_design_receipt_reference=SAMPLING_REFERENCE, + sampling_design_receipt_version=3, + sampling_design_receipt_digest=SAMPLING_DIGEST, + sampled_occurrence_set_digest=SAMPLED_SET_DIGEST, + selection_probability_set_digest=SELECTION_PROBABILITY_SET_DIGEST, + selection_stage_count=2, + base_weight_method_code="inverse_inclusion_probability", + base_weight_method_version=1, + base_weight_artifact_digest=BASE_ARTIFACT_DIGEST, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=6, + owner_contract_digest=OWNER_DIGEST, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=read_port, + ) + + +def test_owner_port_cannot_append_hidden_tuple_fields_to_exact_base_weight_record() -> None: + """Reject exact-typed owner evidence with coordinates outside the canonical tuple.""" + valid = _record() + forged = tuple.__new__( + BaseWeightAuthorityRecord, + (*tuple(valid), "hidden-unreviewed-owner-coordinate"), + ) + + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(_ReadPort(forged)) + + +def test_malformed_exact_base_weight_record_maps_to_integrity_error() -> None: + """Map a truncated exact-typed owner tuple to the domain integrity boundary.""" + valid = _record() + forged = tuple.__new__(BaseWeightAuthorityRecord, tuple(valid)[:-1]) + + with pytest.raises(BaseWeightAuthorityIntegrityError): + _resolve(_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_base_weight_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_base_weight_authority_view_issuance_integrity.py new file mode 100644 index 000000000..1a181adaa --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_authority_view_issuance_integrity.py @@ -0,0 +1,90 @@ +"""Regression contract for base-weight authorized-view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.base_weight_authority as authority_module +from orgmetra_workforce_validation_api.base_weight_authority import ( + BaseWeightAuthorityIntegrityError, + BaseWeightAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_low_level_tuple_construction_cannot_issue_base_weight_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + BaseWeightAuthorityView, + ( + TENANT.int, + STUDY.int, + (("base_weight_artifact_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_base_weight_view() -> None: + """Require the resolver seal before any raw exact-runtime object exposes state.""" + unsealed = object.__new__(BaseWeightAuthorityView) + + for attribute in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + BaseWeightAuthorityIntegrityError, + match="was not issued by resolve_base_weight_authority", + ): + getattr(unsealed, attribute) + + +def test_wrong_issuance_marker_cannot_expose_base_weight_view() -> None: + """Reject marker-shaped raw objects that did not originate from the resolver.""" + forged = object.__new__(BaseWeightAuthorityView) + object.__setattr__(forged, "_issuance_marker", object()) + + with pytest.raises( + BaseWeightAuthorityIntegrityError, + match="was not issued by resolve_base_weight_authority", + ): + _ = forged.fields + + +def test_importable_marker_cannot_mint_base_weight_view() -> None: + """Reject a caller-populated view and keep its sealing capability out of module state.""" + assert not hasattr(authority_module, "_BASE_WEIGHT_AUTHORITY_VIEW_ISSUANCE_MARKER") + forged = object.__new__(BaseWeightAuthorityView) + object.__setattr__(forged, "_tenant_identity", TENANT.int) + object.__setattr__(forged, "_study_identity", STUDY.int) + object.__setattr__( + forged, + "_fields", + (("base_weight_artifact_digest", "6" * 64),), + ) + object.__setattr__( + forged, + "_issuance_marker", + getattr( + authority_module, + "_BASE_WEIGHT_AUTHORITY_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + + with pytest.raises( + BaseWeightAuthorityIntegrityError, + match="was not issued by resolve_base_weight_authority", + ): + _ = forged.fields + + +def test_raw_base_weight_view_rejects_public_mutation_and_deletion() -> None: + """Keep projection state immutable even when callers allocate the exact runtime type.""" + raw = object.__new__(BaseWeightAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw._fields diff --git a/services/workforce-validation-api/tests/test_base_weight_currentness.py b/services/workforce-validation-api/tests/test_base_weight_currentness.py new file mode 100644 index 000000000..d2c6a61d0 --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_currentness.py @@ -0,0 +1,111 @@ +"""Currentness contract for released base/design-weight evidence.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.base_weight_authority import ( + BaseWeightAuthorityIntegrityError, + BaseWeightAuthorityRecord, + _READ_FIELDS, + resolve_base_weight_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +RELEASED = datetime(2026, 9, 17, 7, 30, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 7, 30, tzinfo=timezone.utc) + + +class _ReadPort: + def __init__(self, record: BaseWeightAuthorityRecord) -> None: + self.record = record + + def read_base_weight_authority(self, **_: object) -> BaseWeightAuthorityRecord: + return self.record + + +def _record(**overrides: object) -> BaseWeightAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "base_weight_evidence_receipt_reference": "base_weight_evidence_receipt:11111111-1111-4111-8111-111111111111", + "base_weight_evidence_receipt_digest": "1" * 64, + "evidence_version": 1, + "source_universe_receipt_reference": "source_universe_receipt:22222222-2222-4222-8222-222222222222", + "source_universe_receipt_version": 4, + "source_universe_receipt_digest": "2" * 64, + "source_universe_released_at": datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc), + "sampling_design_receipt_reference": "sampling_design_receipt:33333333-3333-4333-8333-333333333333", + "sampling_design_receipt_version": 3, + "sampling_design_receipt_digest": "3" * 64, + "sampling_design_released_at": datetime(2026, 9, 17, 6, 30, tzinfo=timezone.utc), + "sampled_occurrence_set_digest": "4" * 64, + "selection_probability_set_digest": "5" * 64, + "selection_stage_count": 2, + "base_weight_method_code": "inverse_inclusion_probability", + "base_weight_method_version": 1, + "base_weight_artifact_digest": "6" * 64, + "constructed_at": datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc), + "owner_contract_reference": "released_owner_contract:44444444-4444-4444-8444-444444444444", + "owner_contract_version": 6, + "owner_contract_digest": "7" * 64, + "owner_contract_released_at": datetime(2026, 9, 17, 6, 45, tzinfo=timezone.utc), + "released_at": RELEASED, + "superseded_at": CUTOVER, + } + values.update(overrides) + return BaseWeightAuthorityRecord(**values) + + +def _resolve(record: BaseWeightAuthorityRecord, *, used_at: datetime) -> None: + values = dict(record.fields) + for owner_resolved_field in ( + "source_universe_released_at", + "sampling_design_released_at", + "owner_contract_released_at", + ): + values.pop(owner_resolved_field) + resolve_base_weight_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="base-weight-authority-read-v1", + resource_kind="base_weight_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=_READ_FIELDS, + ), + read_port=_ReadPort(record), + **values, + ) + + +def test_supersession_cutover_is_owner_resolved_and_half_open() -> None: + record = _record() + _resolve(record, used_at=CUTOVER - timedelta(microseconds=1)) + + with pytest.raises(BaseWeightAuthorityIntegrityError, match="superseded"): + _resolve(record, used_at=CUTOVER) + + +def test_superseded_at_requires_timezone_and_post_release_cutover() -> None: + with pytest.raises(ValueError): + _record(superseded_at=datetime(2026, 9, 18, 7, 30)) + + with pytest.raises(ValueError, match="later than released_at"): + _record(superseded_at=RELEASED) diff --git a/services/workforce-validation-api/tests/test_base_weight_owner_contract_chronology.py b/services/workforce-validation-api/tests/test_base_weight_owner_contract_chronology.py new file mode 100644 index 000000000..442cebc11 --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_owner_contract_chronology.py @@ -0,0 +1,93 @@ +"""Fail closed when base-weight evidence predates its released prerequisites.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.base_weight_authority import ( + BaseWeightAuthorityRecord, + resolve_base_weight_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +SOURCE_RELEASED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +SAMPLING_RELEASED_AT = datetime(2026, 9, 17, 6, 30, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 17, 6, 45, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 7, 30, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> BaseWeightAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "base_weight_evidence_receipt_reference": ( + "base_weight_evidence_receipt:11111111-1111-4111-8111-111111111111" + ), + "base_weight_evidence_receipt_digest": "1" * 64, + "evidence_version": 1, + "source_universe_receipt_reference": ( + "source_universe_receipt:22222222-2222-4222-8222-222222222222" + ), + "source_universe_receipt_version": 4, + "source_universe_receipt_digest": "2" * 64, + "source_universe_released_at": SOURCE_RELEASED_AT, + "sampling_design_receipt_reference": ( + "sampling_design_receipt:33333333-3333-4333-8333-333333333333" + ), + "sampling_design_receipt_version": 3, + "sampling_design_receipt_digest": "3" * 64, + "sampling_design_released_at": SAMPLING_RELEASED_AT, + "sampled_occurrence_set_digest": "4" * 64, + "selection_probability_set_digest": "5" * 64, + "selection_stage_count": 2, + "base_weight_method_code": "inverse_inclusion_probability", + "base_weight_method_version": 1, + "base_weight_artifact_digest": "6" * 64, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": ( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + "owner_contract_version": 6, + "owner_contract_digest": "7" * 64, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return BaseWeightAuthorityRecord(**values) + + +def test_release_instants_are_preserved_as_authority_provenance() -> None: + fields = dict(_record().fields) + + assert fields["source_universe_released_at"] == SOURCE_RELEASED_AT + assert fields["sampling_design_released_at"] == SAMPLING_RELEASED_AT + assert fields["owner_contract_released_at"] == OWNER_CONTRACT_RELEASED_AT + + +def test_release_instants_are_not_caller_asserted_resolver_coordinates() -> None: + parameters = signature(resolve_base_weight_authority).parameters + + assert "source_universe_released_at" not in parameters + assert "sampling_design_released_at" not in parameters + assert "owner_contract_released_at" not in parameters + + +def test_released_prerequisite_timestamp_and_chronology_fail_closed() -> None: + with pytest.raises(ValueError): + _record(source_universe_released_at=datetime(2026, 9, 17, 6, 0)) + with pytest.raises(ValueError): + _record(sampling_design_released_at=datetime(2026, 9, 17, 6, 30)) + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 17, 6, 45)) + with pytest.raises(ValueError): + _record(source_universe_released_at=CONSTRUCTED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record(sampling_design_released_at=CONSTRUCTED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(seconds=1)) diff --git a/services/workforce-validation-api/tests/test_base_weight_read_port_lookup_contract.py b/services/workforce-validation-api/tests/test_base_weight_read_port_lookup_contract.py new file mode 100644 index 000000000..8e7f7e17c --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_read_port_lookup_contract.py @@ -0,0 +1,47 @@ +"""Guard the exact immutable lookup key for base-weight owner evidence.""" + +from __future__ import annotations + +from inspect import signature + +from orgmetra_workforce_validation_api.base_weight_authority import ( + BaseWeightAuthorityReadPort, +) + + +def test_base_weight_read_port_requires_complete_reproducibility_tuple() -> None: + """Require every caller-known coordinate needed to select one owner record.""" + parameters = set(signature(BaseWeightAuthorityReadPort.read_base_weight_authority).parameters) + + required_lookup_coordinates = { + "tenant_record_id", + "validity_study_id", + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "evidence_version", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "sampled_occurrence_set_digest", + "selection_probability_set_digest", + "selection_stage_count", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + } + assert required_lookup_coordinates <= parameters + + owner_resolved_chronology = { + "source_universe_released_at", + "sampling_design_released_at", + "owner_contract_released_at", + "released_at", + } + assert parameters.isdisjoint(owner_resolved_chronology) diff --git a/services/workforce-validation-api/tests/test_base_weight_supersession_authority.py b/services/workforce-validation-api/tests/test_base_weight_supersession_authority.py new file mode 100644 index 000000000..e7bad6ce6 --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_supersession_authority.py @@ -0,0 +1,324 @@ +"""Append-only correction contract for released base/design-weight authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.base_weight_supersession_authority import ( + BaseWeightSupersessionAuthorityIntegrityError, + BaseWeightSupersessionAuthorityNotFound, + BaseWeightSupersessionAuthorityReadPort, + BaseWeightSupersessionAuthorityRecord, + BaseWeightSupersessionAuthorityView, + resolve_base_weight_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +RECEIPT = "base_weight_evidence_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR = "base_weight_evidence_receipt:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "2" * 64 +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED = datetime(2026, 9, 17, 6, 45, tzinfo=timezone.utc) +RELEASED = datetime(2026, 9, 17, 7, 30, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 7, 30, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_base_weight_evidence_receipt_reference", + "successor_base_weight_evidence_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_base_weight_supersession_authority(self, **coordinates: object) -> object: + self.calls.append(dict(coordinates)) + return self.result + + +class _ProtocolOnly(BaseWeightSupersessionAuthorityReadPort): + pass + + +class _DescriptorReadPort: + @property + def read_base_weight_supersession_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +class _NoReadMethod: + pass + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="base-weight-supersession-read-v1", + resource_kind="base_weight_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> BaseWeightSupersessionAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "base_weight_evidence_receipt_reference": RECEIPT, + "base_weight_evidence_receipt_digest": DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED, + "released_at": RELEASED, + "superseded_at": CUTOVER, + "successor_base_weight_evidence_receipt_reference": SUCCESSOR, + "successor_base_weight_evidence_receipt_digest": SUCCESSOR_DIGEST, + "successor_evidence_version": 1, + "successor_released_at": CUTOVER, + } + values.update(overrides) + return BaseWeightSupersessionAuthorityRecord(**values) + + +def _resolve(*, read_port: object, used_at: datetime, **overrides: object): + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "base_weight_evidence_receipt_reference": RECEIPT, + "base_weight_evidence_receipt_digest": DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "used_at": used_at, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_base_weight_supersession_authority(**values) + + +def test_successor_edge_requires_complete_atomic_released_coordinates() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(ValueError, match="complete released successor coordinates"): + _record(successor_released_at=None) + with pytest.raises(ValueError, match="later than base-weight receipt release"): + _record(superseded_at=RELEASED) + with pytest.raises(ValueError, match="new reference"): + _record(successor_base_weight_evidence_receipt_reference=RECEIPT) + with pytest.raises(ValueError, match="new evidence"): + _record(successor_base_weight_evidence_receipt_digest=DIGEST) + with pytest.raises(ValueError, match="successor_evidence_version must remain 1"): + _record(successor_evidence_version=2) + with pytest.raises(ValueError, match="released after its predecessor"): + _record(superseded_at=RELEASED + timedelta(seconds=1), successor_released_at=RELEASED) + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=CUTOVER - timedelta(seconds=1)) + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=CUTOVER + timedelta(seconds=1)) + + +def test_chronology_requires_released_owner_and_timezone_aware_instants() -> None: + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 17, 6, 45)) + with pytest.raises(ValueError): + _record(released_at=datetime(2026, 9, 17, 7, 30)) + with pytest.raises(ValueError): + _record(superseded_at=datetime(2026, 9, 18, 7, 30)) + with pytest.raises(ValueError): + _record(successor_released_at=datetime(2026, 9, 18, 7, 30)) + + +def test_historical_use_is_allowed_but_cutover_use_fails_closed() -> None: + record = _record() + port = _ReadPort(record) + view = _resolve(read_port=port, used_at=CUTOVER - timedelta(microseconds=1)) + + assert view.validity_study_id == STUDY + + assert isinstance(port, BaseWeightSupersessionAuthorityReadPort) + assert port.calls == [ + { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "base_weight_evidence_receipt_reference": RECEIPT, + "base_weight_evidence_receipt_digest": DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + } + ] + assert ("base_weight_evidence_receipt_reference", RECEIPT) in view.fields + assert ("released_at", RELEASED) in view.fields + assert ("superseded_at", CUTOVER) in view.fields + assert all(not name.startswith("successor_") for name, _ in view.fields) + + with pytest.raises(BaseWeightSupersessionAuthorityIntegrityError, match="superseded"): + _resolve(read_port=_ReadPort(record), used_at=CUTOVER) + + +def test_open_interval_without_successor_remains_current() -> None: + record = _record( + superseded_at=None, + successor_base_weight_evidence_receipt_reference=None, + successor_base_weight_evidence_receipt_digest=None, + successor_evidence_version=None, + successor_released_at=None, + ) + view = _resolve(read_port=_ReadPort(record), used_at=CUTOVER + timedelta(days=30)) + assert ("superseded_at", None) in view.fields + + +def test_missing_noncanonical_and_pre_release_evidence_fail_closed() -> None: + with pytest.raises(BaseWeightSupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None), used_at=RELEASED) + with pytest.raises(BaseWeightSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object()), used_at=RELEASED) + with pytest.raises(BaseWeightSupersessionAuthorityIntegrityError, match="released before scientific use"): + _resolve(read_port=_ReadPort(_record()), used_at=RELEASED - timedelta(seconds=1)) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"base_weight_evidence_receipt_reference": SUCCESSOR}, + {"base_weight_evidence_receipt_digest": "4" * 64}, + {"owner_contract_reference": "released_owner_contract:44444444-4444-4444-8444-444444444444"}, + {"owner_contract_version": 2}, + {"owner_contract_digest": "5" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate(record_overrides: dict[str, object]) -> None: + if "base_weight_evidence_receipt_reference" in record_overrides: + record_overrides = { + **record_overrides, + "successor_base_weight_evidence_receipt_reference": RECEIPT, + } + with pytest.raises(BaseWeightSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides)), used_at=RELEASED) + + +def test_authorization_denial_precedes_owner_read() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, used_at=RELEASED, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("base_weight_evidence_receipt_reference", "wrong:receipt", ValueError), + ("base_weight_evidence_receipt_digest", "ABC", ValueError), + ("evidence_version", 2, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "3" * 63, ValueError), + ("used_at", datetime(2026, 9, 18, 7, 0), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **{"used_at": RELEASED, **overrides}) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_immutable_and_uuid_views_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(record, "released_at", CUTOVER) + + view = _resolve(read_port=_ReadPort(record), used_at=RELEASED) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + + with pytest.raises(TypeError): + BaseWeightSupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_exact_typed_hidden_tail_record_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + BaseWeightSupersessionAuthorityRecord, + tuple(canonical) + (("hidden_owner_coordinate", "must-not-normalize-away"),), + ) + + with pytest.raises(BaseWeightSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged), used_at=RELEASED) + + +def test_exact_typed_truncated_record_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__(BaseWeightSupersessionAuthorityRecord, tuple(canonical)[:-1]) + + with pytest.raises(BaseWeightSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged), used_at=RELEASED) diff --git a/services/workforce-validation-api/tests/test_base_weight_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_base_weight_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..77359e337 --- /dev/null +++ b/services/workforce-validation-api/tests/test_base_weight_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,93 @@ +"""Regression contract for base-weight supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.base_weight_supersession_authority as authority_module +from orgmetra_workforce_validation_api.base_weight_supersession_authority import ( + BaseWeightSupersessionAuthorityIntegrityError, + BaseWeightSupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_low_level_tuple_construction_cannot_issue_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + BaseWeightSupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("base_weight_evidence_receipt_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(BaseWeightSupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + BaseWeightSupersessionAuthorityIntegrityError, + match="was not issued by resolve_base_weight_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(BaseWeightSupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + BaseWeightSupersessionAuthorityIntegrityError, + match="was not issued by resolve_base_weight_supersession_authority", + ): + _ = forged_view.fields + + +def test_importable_marker_cannot_mint_supersession_view() -> None: + """Keep view-sealing authority out of ordinary module state.""" + assert not hasattr( + authority_module, + "_BASE_WEIGHT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + forged_view = object.__new__(BaseWeightSupersessionAuthorityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__( + forged_view, + "_fields", + (("base_weight_evidence_receipt_digest", "6" * 64),), + ) + object.__setattr__( + forged_view, + "_issuance_marker", + getattr( + authority_module, + "_BASE_WEIGHT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + + with pytest.raises( + BaseWeightSupersessionAuthorityIntegrityError, + match="was not issued by resolve_base_weight_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(BaseWeightSupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_built_wheel_dist_info_identity.py b/services/workforce-validation-api/tests/test_built_wheel_dist_info_identity.py new file mode 100644 index 000000000..51cba8200 --- /dev/null +++ b/services/workforce-validation-api/tests/test_built_wheel_dist_info_identity.py @@ -0,0 +1,108 @@ +"""Reject wheels whose dist-info directory does not match the built distribution identity.""" + +from __future__ import annotations + +import csv +import importlib.util +import io +from pathlib import Path +import zipfile + +import pytest + + +_CONTRACT_PATH = Path(__file__).with_name("test_package_metadata_compatibility.py") +_SPEC = importlib.util.spec_from_file_location( + "_workforce_package_metadata_contract_dist_info", + _CONTRACT_PATH, +) +assert _SPEC is not None and _SPEC.loader is not None +_CONTRACT = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(_CONTRACT) + + +def _write_wheel( + wheelhouse: Path, + *, + filename: str, + package_root: str, + dist_info_root: str, + metadata: str, + include_py_typed: bool, +) -> None: + """Create one internally self-consistent wheel with a selectable dist-info root.""" + members: dict[str, bytes] = { + f"{package_root}/__init__.py": b"", + f"{dist_info_root}/METADATA": metadata.encode("utf-8"), + f"{dist_info_root}/WHEEL": ( + "Wheel-Version: 1.0\n" + "Generator: orgmetra-test-fixture\n" + "Root-Is-Purelib: true\n" + "Tag: py3-none-any\n\n" + ).encode("utf-8"), + } + if include_py_typed: + members[f"{package_root}/py.typed"] = b"" + + record_path = f"{dist_info_root}/RECORD" + output = io.StringIO() + writer = csv.writer(output, lineterminator="\n") + for member_path in sorted(members): + member = members[member_path] + writer.writerow((member_path, _CONTRACT._record_hash(member), str(len(member)))) + writer.writerow((record_path, "", "")) + members[record_path] = output.getvalue().encode("utf-8") + + with zipfile.ZipFile(wheelhouse / filename, "w") as archive: + for member_path, content in members.items(): + archive.writestr(member_path, content) + + +def test_hash_locked_acceptance_rejects_mismatched_dist_info_identity_before_hashing( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A foreign dist-info directory must fail before outer artifact hashing can run.""" + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + _write_wheel( + wheelhouse, + filename="orgmetra_keyverse_adapter-0.1.0-py3-none-any.whl", + package_root="orgmetra_keyverse_adapter", + dist_info_root="orgmetra_keyverse_adapter-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-keyverse-adapter\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Provides-Extra: test\n" + "Requires-Dist: pytest>=8.3; extra == 'test'\n" + "Requires-Dist: pytest-cov>=5.0; extra == 'test'\n\n" + ), + include_py_typed=False, + ) + _write_wheel( + wheelhouse, + filename="orgmetra_workforce_validation_api-0.1.0-py3-none-any.whl", + package_root="orgmetra_workforce_validation_api", + dist_info_root="foreign_distribution-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-workforce-validation-api\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Requires-Dist: orgmetra-keyverse-adapter==0.1.0\n\n" + ), + include_py_typed=True, + ) + + def _forbid_outer_hash(_path: Path) -> str: + raise AssertionError("outer hash must not run for a foreign dist-info root") + + monkeypatch.setattr(_CONTRACT, "_sha256", _forbid_outer_hash) + with pytest.raises(AssertionError, match="dist-info identity"): + _CONTRACT._locked_wheel_requirements( + wheelhouse, + service_version="0.1.0", + keyverse_version="0.1.0", + ) diff --git a/services/workforce-validation-api/tests/test_built_wheel_metadata_contract.py b/services/workforce-validation-api/tests/test_built_wheel_metadata_contract.py new file mode 100644 index 000000000..5db9bc86f --- /dev/null +++ b/services/workforce-validation-api/tests/test_built_wheel_metadata_contract.py @@ -0,0 +1,302 @@ +"""Reject built wheels whose metadata or RECORD detaches reviewed artifact truth.""" + +from __future__ import annotations + +import csv +import importlib.util +import io +from pathlib import Path +import zipfile + +import pytest + + +_CONTRACT_PATH = Path(__file__).with_name("test_package_metadata_compatibility.py") +_SPEC = importlib.util.spec_from_file_location( + "_workforce_package_metadata_contract", + _CONTRACT_PATH, +) +assert _SPEC is not None and _SPEC.loader is not None +_CONTRACT = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(_CONTRACT) + + +def _write_wheel( + wheelhouse: Path, + *, + filename: str, + package_root: str, + dist_info_root: str, + metadata: str, + include_py_typed: bool, + break_record_hash: bool = False, +) -> None: + """Create a minimal internally recorded wheel for artifact-contract regressions.""" + members: dict[str, bytes] = { + f"{package_root}/__init__.py": b"", + f"{dist_info_root}/METADATA": metadata.encode("utf-8"), + f"{dist_info_root}/WHEEL": ( + "Wheel-Version: 1.0\n" + "Generator: orgmetra-test-fixture\n" + "Root-Is-Purelib: true\n" + "Tag: py3-none-any\n\n" + ).encode("utf-8"), + } + if include_py_typed: + members[f"{package_root}/py.typed"] = b"" + + record_path = f"{dist_info_root}/RECORD" + output = io.StringIO() + writer = csv.writer(output, lineterminator="\n") + for member_path in sorted(members): + member = members[member_path] + member_hash = _CONTRACT._record_hash(member) + if break_record_hash and member_path == f"{package_root}/__init__.py": + member_hash = "sha256=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + writer.writerow((member_path, member_hash, str(len(member)))) + writer.writerow((record_path, "", "")) + members[record_path] = output.getvalue().encode("utf-8") + + wheel_path = wheelhouse / filename + with zipfile.ZipFile(wheel_path, "w") as archive: + for member_path, member in members.items(): + archive.writestr(member_path, member) + + +def _keyverse_metadata(*provides_extra_lines: str) -> str: + """Build Keyverse Core Metadata while varying only the declared extra ledger.""" + return "".join( + ( + "Metadata-Version: 2.4\n", + "Name: orgmetra-keyverse-adapter\n", + "Version: 0.1.0\n", + "Requires-Python: >=3.12\n", + *(f"Provides-Extra: {value}\n" for value in provides_extra_lines), + "Requires-Dist: pytest>=8.3; extra == 'test'\n", + "Requires-Dist: pytest-cov>=5.0; extra == 'test'\n\n", + ) + ) + + +def test_hash_locked_acceptance_rejects_service_wheel_missing_keyverse_dependency( + tmp_path: Path, +) -> None: + """A directly locked Keyverse wheel must not mask missing service dependency metadata.""" + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + _write_wheel( + wheelhouse, + filename="orgmetra_keyverse_adapter-0.1.0-py3-none-any.whl", + package_root="orgmetra_keyverse_adapter", + dist_info_root="orgmetra_keyverse_adapter-0.1.0.dist-info", + metadata=_keyverse_metadata("test"), + include_py_typed=False, + ) + _write_wheel( + wheelhouse, + filename="orgmetra_workforce_validation_api-0.1.0-py3-none-any.whl", + package_root="orgmetra_workforce_validation_api", + dist_info_root="orgmetra_workforce_validation_api-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-workforce-validation-api\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n\n" + ), + include_py_typed=True, + ) + + with pytest.raises(AssertionError, match="mandatory Keyverse dependency"): + _CONTRACT._locked_wheel_requirements( + wheelhouse, + service_version="0.1.0", + keyverse_version="0.1.0", + ) + + +def test_hash_locked_acceptance_rejects_unreviewed_inactive_dependency( + tmp_path: Path, +) -> None: + """Built metadata must not gain a marker-disabled dependency absent from source truth.""" + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + _write_wheel( + wheelhouse, + filename="orgmetra_keyverse_adapter-0.1.0-py3-none-any.whl", + package_root="orgmetra_keyverse_adapter", + dist_info_root="orgmetra_keyverse_adapter-0.1.0.dist-info", + metadata=_keyverse_metadata("test"), + include_py_typed=False, + ) + _write_wheel( + wheelhouse, + filename="orgmetra_workforce_validation_api-0.1.0-py3-none-any.whl", + package_root="orgmetra_workforce_validation_api", + dist_info_root="orgmetra_workforce_validation_api-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-workforce-validation-api\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Requires-Dist: orgmetra-keyverse-adapter==0.1.0\n" + "Requires-Dist: unreviewed-package>=1; python_version < '3.0'\n\n" + ), + include_py_typed=True, + ) + + with pytest.raises(AssertionError, match="reviewed project dependencies"): + _CONTRACT._locked_wheel_requirements( + wheelhouse, + service_version="0.1.0", + keyverse_version="0.1.0", + ) + + +def test_hash_locked_acceptance_preserves_reviewed_optional_dependencies( + tmp_path: Path, +) -> None: + """PEP 621 optional dependencies must remain reviewed metadata, not false positives.""" + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + _write_wheel( + wheelhouse, + filename="orgmetra_keyverse_adapter-0.1.0-py3-none-any.whl", + package_root="orgmetra_keyverse_adapter", + dist_info_root="orgmetra_keyverse_adapter-0.1.0.dist-info", + metadata=_keyverse_metadata("test"), + include_py_typed=False, + ) + _write_wheel( + wheelhouse, + filename="orgmetra_workforce_validation_api-0.1.0-py3-none-any.whl", + package_root="orgmetra_workforce_validation_api", + dist_info_root="orgmetra_workforce_validation_api-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-workforce-validation-api\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Requires-Dist: orgmetra-keyverse-adapter==0.1.0\n\n" + ), + include_py_typed=True, + ) + + locked_requirements, wheels_by_name = _CONTRACT._locked_wheel_requirements( + wheelhouse, + service_version="0.1.0", + keyverse_version="0.1.0", + ) + + assert "orgmetra-keyverse-adapter==0.1.0" in locked_requirements + assert set(wheels_by_name) == { + "orgmetra-keyverse-adapter", + "orgmetra-workforce-validation-api", + } + + +@pytest.mark.parametrize( + "provides_extra_lines", + [ + (), + ("testing",), + ("test", "test"), + ], + ids=("missing", "different-name", "duplicate"), +) +def test_hash_locked_acceptance_rejects_optional_extra_ledger_drift( + tmp_path: Path, + provides_extra_lines: tuple[str, ...], +) -> None: + """Extra-gated requirements cannot substitute for the reviewed Provides-Extra ledger.""" + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + _write_wheel( + wheelhouse, + filename="orgmetra_keyverse_adapter-0.1.0-py3-none-any.whl", + package_root="orgmetra_keyverse_adapter", + dist_info_root="orgmetra_keyverse_adapter-0.1.0.dist-info", + metadata=_keyverse_metadata(*provides_extra_lines), + include_py_typed=False, + ) + _write_wheel( + wheelhouse, + filename="orgmetra_workforce_validation_api-0.1.0-py3-none-any.whl", + package_root="orgmetra_workforce_validation_api", + dist_info_root="orgmetra_workforce_validation_api-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-workforce-validation-api\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Requires-Dist: orgmetra-keyverse-adapter==0.1.0\n\n" + ), + include_py_typed=True, + ) + + with pytest.raises(AssertionError, match="METADATA extras"): + _CONTRACT._locked_wheel_requirements( + wheelhouse, + service_version="0.1.0", + keyverse_version="0.1.0", + ) + + +def test_hash_locked_acceptance_rejects_wheel_with_invalid_record_hash( + tmp_path: Path, +) -> None: + """The exact install-lock helper must reject a false internal installation RECORD.""" + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + _write_wheel( + wheelhouse, + filename="orgmetra_keyverse_adapter-0.1.0-py3-none-any.whl", + package_root="orgmetra_keyverse_adapter", + dist_info_root="orgmetra_keyverse_adapter-0.1.0.dist-info", + metadata=_keyverse_metadata("test"), + include_py_typed=False, + ) + _write_wheel( + wheelhouse, + filename="orgmetra_workforce_validation_api-0.1.0-py3-none-any.whl", + package_root="orgmetra_workforce_validation_api", + dist_info_root="orgmetra_workforce_validation_api-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-workforce-validation-api\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Requires-Dist: orgmetra-keyverse-adapter==0.1.0\n\n" + ), + include_py_typed=True, + break_record_hash=True, + ) + + with pytest.raises(AssertionError, match="RECORD sha256 mismatch"): + _CONTRACT._locked_wheel_requirements( + wheelhouse, + service_version="0.1.0", + keyverse_version="0.1.0", + ) + + +def test_record_rejects_normalization_alias_member_paths(tmp_path: Path) -> None: + """Raw-distinct ZIP paths that normalize to one install path must fail closed.""" + wheel_path = tmp_path / "alias-0.1.0-py3-none-any.whl" + members = { + "alias/__init__.py": b"canonical", + "./alias/__init__.py": b"ambiguous", + } + record_path = "alias-0.1.0.dist-info/RECORD" + output = io.StringIO() + writer = csv.writer(output, lineterminator="\n") + for member_path, content in members.items(): + writer.writerow((member_path, _CONTRACT._record_hash(content), str(len(content)))) + writer.writerow((record_path, "", "")) + + with zipfile.ZipFile(wheel_path, "w") as archive: + for member_path, content in members.items(): + archive.writestr(member_path, content) + archive.writestr(record_path, output.getvalue().encode("utf-8")) + + with pytest.raises(AssertionError, match="non-canonical"): + _CONTRACT._validate_wheel_record(wheel_path) diff --git a/services/workforce-validation-api/tests/test_built_wheel_record_integrity.py b/services/workforce-validation-api/tests/test_built_wheel_record_integrity.py new file mode 100644 index 000000000..4e08e9092 --- /dev/null +++ b/services/workforce-validation-api/tests/test_built_wheel_record_integrity.py @@ -0,0 +1,83 @@ +"""Verify that shipped owned wheels carry internally truthful installation RECORDs.""" + +from __future__ import annotations + +import importlib.util +from importlib.metadata import version as installed_version +from pathlib import Path +import shutil +import subprocess +import sys + +from packaging.version import Version + + +_TEST_ROOT = Path(__file__).resolve().parent +_METADATA_PATH = _TEST_ROOT / "test_package_metadata_compatibility.py" + +_METADATA_SPEC = importlib.util.spec_from_file_location( + "_workforce_package_metadata_contract_for_record", + _METADATA_PATH, +) +assert _METADATA_SPEC is not None and _METADATA_SPEC.loader is not None +_METADATA_CONTRACT = importlib.util.module_from_spec(_METADATA_SPEC) +_METADATA_SPEC.loader.exec_module(_METADATA_CONTRACT) + + +def test_built_owned_wheels_have_complete_verified_records(tmp_path: Path) -> None: + """Build the exact owned distributions and verify every installed member against RECORD.""" + backend_requirement = _METADATA_CONTRACT._service_build_backend_requirement() + assert Version(installed_version("setuptools")) in backend_requirement.specifier, ( + "canonical test/build toolchain must install the exact reviewed setuptools backend" + ) + + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + build_sources = tmp_path / "build-sources" + build_sources.mkdir() + environment = _METADATA_CONTRACT._subprocess_environment() + source_roots = ( + _METADATA_CONTRACT._KEYVERSE_ROOT, + _METADATA_CONTRACT._SERVICE_ROOT, + ) + for source_root in source_roots: + build_root = build_sources / source_root.name + shutil.copytree( + source_root, + build_root, + ignore=shutil.ignore_patterns( + "__pycache__", + ".pytest_cache", + ".coverage", + "build", + "dist", + "*.egg-info", + "*.pyc", + ), + ) + subprocess.run( + [ + sys.executable, + "-m", + "pip", + "wheel", + "--no-index", + "--no-cache-dir", + "--no-deps", + "--no-build-isolation", + "--wheel-dir", + str(wheelhouse), + str(build_root), + ], + cwd=tmp_path, + env=environment, + check=True, + ) + + wheel_paths = tuple(sorted(wheelhouse.iterdir())) + assert len(wheel_paths) == 2, "RECORD acceptance must inspect both owned built wheels" + for wheel_path in wheel_paths: + _METADATA_CONTRACT._validate_wheel_record(wheel_path) + assert all(not (source_root / "build").exists() for source_root in source_roots), ( + "wheel RECORD acceptance must not mutate repository source roots" + ) diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_authority.py b/services/workforce-validation-api/tests/test_calibration_adjustment_authority.py new file mode 100644 index 000000000..0f094f516 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_authority.py @@ -0,0 +1,591 @@ +"""Fail-closed contract for released typed calibration-adjustment authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.calibration_adjustment_authority as authority_module +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.calibration_adjustment_authority import ( + CalibrationAdjustmentAuthorityIntegrityError, + CalibrationAdjustmentAuthorityNotFound, + CalibrationAdjustmentAuthorityReadPort, + CalibrationAdjustmentAuthorityRecord, + CalibrationAdjustmentAuthorityView, + resolve_calibration_adjustment_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT_REFERENCE = "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +AUXILIARY_AUTHORITY_REFERENCE = "scientific_auxiliary_authority:33333333-3333-4333-8333-333333333333" +AUXILIARY_PROJECTION_REFERENCE = "calibration_auxiliary_projection:44444444-4444-4444-8444-444444444444" +AUXILIARY_PURPOSE_REFERENCE = "scientific_data_use_purpose:55555555-5555-4555-8555-555555555555" +AUXILIARY_OWNER_CONTRACT_REFERENCE = "released_owner_contract:66666666-6666-4666-8666-666666666666" +AUXILIARY_AUTHORIZATION_REFERENCE = "scientific_data_authorization:77777777-7777-4777-8777-777777777777" +AUXILIARY_USE_REFERENCE = "scientific_use_receipt:88888888-8888-4888-8888-888888888888" +BENCHMARK_RECEIPT_REFERENCE = "calibration_benchmark_receipt:99999999-9999-4999-8999-999999999999" +BENCHMARK_OWNER_CONTRACT_REFERENCE = "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +RECEIPT_DIGEST = "1" * 64 +TARGET_POPULATION_DIGEST = "a" * 64 +AUXILIARY_PROJECTION_DIGEST = "2" * 64 +AUXILIARY_PURPOSE_DIGEST = "3" * 64 +AUXILIARY_OWNER_CONTRACT_DIGEST = "4" * 64 +AUXILIARY_AUTHORIZATION_DIGEST = "5" * 64 +AUXILIARY_USE_DIGEST = "6" * 64 +BENCHMARK_RECEIPT_DIGEST = "7" * 64 +BENCHMARK_OWNER_CONTRACT_DIGEST = "8" * 64 +CONSTRAINTS_DIGEST = "9" * 64 +INPUT_WEIGHT_DIGEST = "b" * 64 +OUTPUT_WEIGHT_DIGEST = "c" * 64 +FALLBACK_RULE_DIGEST = "d" * 64 +FALLBACK_CONFIGURATION_DIGEST = "e" * 64 +OWNER_CONTRACT_DIGEST = "f" * 64 +ANALYSIS_WINDOW_REFERENCE = "analysis_window:2026q3" +AUXILIARY_USE_AT = datetime(2026, 9, 16, 11, 0, tzinfo=timezone.utc) +BENCHMARK_REFERENCE_AT = datetime(2026, 9, 16, 11, 30, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "calibration_receipt_reference", + "calibration_receipt_digest", + "evidence_version", + "target_population_digest", + "analysis_window_reference", + "auxiliary_authority_reference", + "auxiliary_projection_reference", + "auxiliary_projection_version", + "auxiliary_projection_digest", + "auxiliary_purpose_reference", + "auxiliary_purpose_digest", + "auxiliary_owner_contract_reference", + "auxiliary_owner_contract_version", + "auxiliary_owner_contract_digest", + "auxiliary_authorization_receipt_reference", + "auxiliary_authorization_receipt_digest", + "auxiliary_scientific_use_receipt_reference", + "auxiliary_scientific_use_receipt_digest", + "auxiliary_scientific_use_at", + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_receipt_digest", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_reference_at", + "algorithm_reference", + "algorithm_version", + "constraints_digest", + "termination_code", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "constructed_at", + "fallback_reason_code", + "fallback_rule_reference", + "fallback_rule_digest", + "fallback_algorithm_reference", + "fallback_algorithm_version", + "fallback_configuration_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + } +) + + +class _ReadPort: + """Return configured calibration authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_calibration_adjustment_authority(self, **coordinates: object) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(CalibrationAdjustmentAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must be rejected without executing it.""" + + @property + def read_calibration_adjustment_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-adjustment-authority-read-v1", + resource_kind="calibration_adjustment_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _fallback_values() -> dict[str, object]: + return { + "termination_code": "fallback_applied", + "fallback_reason_code": "primary_nonconvergence", + "fallback_rule_reference": "calibration_fallback_rule:cell-collapse-v2", + "fallback_rule_digest": FALLBACK_RULE_DIGEST, + "fallback_algorithm_reference": "calibration_algorithm:raking", + "fallback_algorithm_version": 4, + "fallback_configuration_digest": FALLBACK_CONFIGURATION_DIGEST, + } + + +def _record(**overrides: object) -> CalibrationAdjustmentAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": RECEIPT_REFERENCE, + "calibration_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "target_population_digest": TARGET_POPULATION_DIGEST, + "analysis_window_reference": ANALYSIS_WINDOW_REFERENCE, + "auxiliary_authority_reference": AUXILIARY_AUTHORITY_REFERENCE, + "auxiliary_projection_reference": AUXILIARY_PROJECTION_REFERENCE, + "auxiliary_projection_version": 2, + "auxiliary_projection_digest": AUXILIARY_PROJECTION_DIGEST, + "auxiliary_purpose_reference": AUXILIARY_PURPOSE_REFERENCE, + "auxiliary_purpose_digest": AUXILIARY_PURPOSE_DIGEST, + "auxiliary_owner_contract_reference": AUXILIARY_OWNER_CONTRACT_REFERENCE, + "auxiliary_owner_contract_version": 3, + "auxiliary_owner_contract_digest": AUXILIARY_OWNER_CONTRACT_DIGEST, + "auxiliary_authorization_receipt_reference": AUXILIARY_AUTHORIZATION_REFERENCE, + "auxiliary_authorization_receipt_digest": AUXILIARY_AUTHORIZATION_DIGEST, + "auxiliary_scientific_use_receipt_reference": AUXILIARY_USE_REFERENCE, + "auxiliary_scientific_use_receipt_digest": AUXILIARY_USE_DIGEST, + "auxiliary_scientific_use_at": AUXILIARY_USE_AT, + "benchmark_receipt_reference": BENCHMARK_RECEIPT_REFERENCE, + "benchmark_receipt_version": 4, + "benchmark_receipt_digest": BENCHMARK_RECEIPT_DIGEST, + "benchmark_owner_contract_reference": BENCHMARK_OWNER_CONTRACT_REFERENCE, + "benchmark_owner_contract_version": 5, + "benchmark_owner_contract_digest": BENCHMARK_OWNER_CONTRACT_DIGEST, + "benchmark_reference_at": BENCHMARK_REFERENCE_AT, + "algorithm_reference": "calibration_algorithm:generalized_regression", + "algorithm_version": 3, + "constraints_digest": CONSTRAINTS_DIGEST, + "termination_code": "converged", + "input_weight_artifact_digest": INPUT_WEIGHT_DIGEST, + "output_weight_artifact_digest": OUTPUT_WEIGHT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "fallback_reason_code": None, + "fallback_rule_reference": None, + "fallback_rule_digest": None, + "fallback_algorithm_reference": None, + "fallback_algorithm_version": None, + "fallback_configuration_digest": None, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 6, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return CalibrationAdjustmentAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> CalibrationAdjustmentAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": RECEIPT_REFERENCE, + "calibration_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "target_population_digest": TARGET_POPULATION_DIGEST, + "analysis_window_reference": ANALYSIS_WINDOW_REFERENCE, + "auxiliary_authority_reference": AUXILIARY_AUTHORITY_REFERENCE, + "auxiliary_projection_reference": AUXILIARY_PROJECTION_REFERENCE, + "auxiliary_projection_version": 2, + "auxiliary_projection_digest": AUXILIARY_PROJECTION_DIGEST, + "auxiliary_purpose_reference": AUXILIARY_PURPOSE_REFERENCE, + "auxiliary_purpose_digest": AUXILIARY_PURPOSE_DIGEST, + "auxiliary_owner_contract_reference": AUXILIARY_OWNER_CONTRACT_REFERENCE, + "auxiliary_owner_contract_version": 3, + "auxiliary_owner_contract_digest": AUXILIARY_OWNER_CONTRACT_DIGEST, + "auxiliary_authorization_receipt_reference": AUXILIARY_AUTHORIZATION_REFERENCE, + "auxiliary_authorization_receipt_digest": AUXILIARY_AUTHORIZATION_DIGEST, + "auxiliary_scientific_use_receipt_reference": AUXILIARY_USE_REFERENCE, + "auxiliary_scientific_use_receipt_digest": AUXILIARY_USE_DIGEST, + "auxiliary_scientific_use_at": AUXILIARY_USE_AT, + "benchmark_receipt_reference": BENCHMARK_RECEIPT_REFERENCE, + "benchmark_receipt_version": 4, + "benchmark_receipt_digest": BENCHMARK_RECEIPT_DIGEST, + "benchmark_owner_contract_reference": BENCHMARK_OWNER_CONTRACT_REFERENCE, + "benchmark_owner_contract_version": 5, + "benchmark_owner_contract_digest": BENCHMARK_OWNER_CONTRACT_DIGEST, + "benchmark_reference_at": BENCHMARK_REFERENCE_AT, + "algorithm_reference": "calibration_algorithm:generalized_regression", + "algorithm_version": 3, + "constraints_digest": CONSTRAINTS_DIGEST, + "termination_code": "converged", + "input_weight_artifact_digest": INPUT_WEIGHT_DIGEST, + "output_weight_artifact_digest": OUTPUT_WEIGHT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "fallback_reason_code": None, + "fallback_rule_reference": None, + "fallback_rule_digest": None, + "fallback_algorithm_reference": None, + "fallback_algorithm_version": None, + "fallback_configuration_digest": None, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 6, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_calibration_adjustment_authority(**values) + + +def test_fallback_resolution_binds_actual_generating_method() -> None: + fallback = _fallback_values() + record = _record(**fallback) + port = _ReadPort(record) + + view = _resolve(read_port=port, **fallback) + + assert isinstance(port, CalibrationAdjustmentAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["calibration_receipt_digest"] == RECEIPT_DIGEST + assert port.calls[0]["target_population_digest"] == TARGET_POPULATION_DIGEST + assert port.calls[0]["analysis_window_reference"] == ANALYSIS_WINDOW_REFERENCE + assert port.calls[0]["auxiliary_projection_reference"] == AUXILIARY_PROJECTION_REFERENCE + assert port.calls[0]["benchmark_receipt_reference"] == BENCHMARK_RECEIPT_REFERENCE + assert port.calls[0]["fallback_algorithm_reference"] == "calibration_algorithm:raking" + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert ("target_population_digest", TARGET_POPULATION_DIGEST) in view.fields + assert ("analysis_window_reference", ANALYSIS_WINDOW_REFERENCE) in view.fields + assert ("auxiliary_projection_reference", AUXILIARY_PROJECTION_REFERENCE) in view.fields + assert ("benchmark_receipt_reference", BENCHMARK_RECEIPT_REFERENCE) in view.fields + assert ("termination_code", "fallback_applied") in view.fields + assert ("fallback_reason_code", "primary_nonconvergence") in view.fields + assert ("fallback_rule_reference", "calibration_fallback_rule:cell-collapse-v2") in view.fields + assert ("fallback_algorithm_reference", "calibration_algorithm:raking") in view.fields + assert ("fallback_algorithm_version", 4) in view.fields + assert ("fallback_configuration_digest", FALLBACK_CONFIGURATION_DIGEST) in view.fields + assert ("owner_contract_released_at", OWNER_CONTRACT_RELEASED_AT) in view.fields + + +def test_converged_resolution_omits_fallback_only_projection() -> None: + view = _resolve(read_port=_ReadPort(_record())) + + assert ("termination_code", "converged") in view.fields + assert all(not field.startswith("fallback_") for field, _ in view.fields) + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(CalibrationAdjustmentAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"calibration_receipt_digest": "0" * 64}, + {"target_population_digest": "1" * 64}, + {"analysis_window_reference": "analysis_window:2026q4"}, + {"auxiliary_authority_reference": "scientific_auxiliary_authority:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"}, + {"auxiliary_projection_reference": "calibration_auxiliary_projection:cccccccc-cccc-4ccc-8ccc-cccccccccccc"}, + {"auxiliary_projection_version": 8}, + {"auxiliary_projection_digest": "3" * 64}, + {"auxiliary_purpose_digest": "4" * 64}, + {"auxiliary_owner_contract_digest": "5" * 64}, + {"auxiliary_authorization_receipt_digest": "6" * 64}, + {"auxiliary_scientific_use_receipt_digest": "7" * 64}, + {"auxiliary_scientific_use_at": AUXILIARY_USE_AT - timedelta(seconds=1)}, + {"benchmark_receipt_reference": "calibration_benchmark_receipt:dddddddd-dddd-4ddd-8ddd-dddddddddddd"}, + {"benchmark_receipt_version": 9}, + {"benchmark_receipt_digest": "8" * 64}, + {"benchmark_owner_contract_digest": "9" * 64}, + {"benchmark_reference_at": BENCHMARK_REFERENCE_AT - timedelta(seconds=1)}, + {"algorithm_reference": "calibration_algorithm:raking"}, + {"algorithm_version": 9}, + {"constraints_digest": "0" * 64}, + {"input_weight_artifact_digest": "1" * 64}, + {"output_weight_artifact_digest": "2" * 64}, + {"owner_contract_version": 7}, + {"owner_contract_digest": "3" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_owner_evidence_must_be_released_before_scientific_use() -> None: + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _resolve( + read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1))) + ) + + +@pytest.mark.parametrize( + "missing_field", + [ + "fallback_reason_code", + "fallback_rule_reference", + "fallback_rule_digest", + "fallback_algorithm_reference", + "fallback_algorithm_version", + "fallback_configuration_digest", + ], +) +def test_fallback_requires_complete_actual_method_provenance(missing_field: str) -> None: + fallback = _fallback_values() + fallback[missing_field] = None + with pytest.raises(ValueError): + _record(**fallback) + + +def test_converged_receipt_rejects_fallback_only_evidence() -> None: + with pytest.raises(ValueError): + _record(fallback_reason_code="should_not_exist") + + +@pytest.mark.parametrize( + ("key", "value"), + [ + ("evidence_version", 2), + ("target_population_digest", "a" * 63), + ("analysis_window_reference", "wrong:window"), + ("auxiliary_authority_reference", "wrong:authority"), + ("auxiliary_projection_reference", "wrong:projection"), + ("auxiliary_projection_version", 0), + ("auxiliary_purpose_reference", "wrong:purpose"), + ("auxiliary_purpose_digest", "3" * 63), + ("auxiliary_owner_contract_reference", "wrong:contract"), + ("auxiliary_owner_contract_version", 0), + ("auxiliary_owner_contract_digest", "4" * 63), + ("auxiliary_authorization_receipt_reference", "wrong:authorization"), + ("auxiliary_authorization_receipt_digest", "5" * 63), + ("auxiliary_scientific_use_receipt_reference", "wrong:use"), + ("auxiliary_scientific_use_receipt_digest", "6" * 63), + ("auxiliary_scientific_use_at", datetime(2026, 9, 16, 11, 0)), + ("benchmark_receipt_reference", "wrong:benchmark"), + ("benchmark_receipt_version", 0), + ("benchmark_owner_contract_reference", "wrong:contract"), + ("benchmark_owner_contract_version", 0), + ("benchmark_owner_contract_digest", "8" * 63), + ("benchmark_reference_at", datetime(2026, 9, 16, 11, 30)), + ("termination_code", "nonconverged"), + ("algorithm_reference", "wrong:method"), + ("algorithm_version", True), + ("fallback_reason_code", "Primary Failure"), + ("fallback_rule_reference", "wrong:rule"), + ("fallback_rule_digest", "d" * 63), + ("fallback_algorithm_reference", "wrong:algorithm"), + ("fallback_algorithm_version", 0), + ("fallback_configuration_digest", "e" * 65), + ], +) +def test_malformed_calibration_or_fallback_evidence_fails_closed( + key: str, value: object +) -> None: + overrides = _fallback_values() + overrides[key] = value + with pytest.raises(ValueError): + _record(**overrides) + + +def test_supporting_evidence_cannot_postdate_calibration_construction() -> None: + with pytest.raises(ValueError): + _record(auxiliary_scientific_use_at=CONSTRUCTED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record(benchmark_reference_at=CONSTRUCTED_AT + timedelta(seconds=1)) + + +def test_weight_artifact_and_release_chronology_fail_closed() -> None: + with pytest.raises(ValueError): + _record(output_weight_artifact_digest=INPUT_WEIGHT_DIGEST) + + with pytest.raises(ValueError): + _record(released_at=CONSTRUCTED_AT - timedelta(seconds=1)) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("calibration_receipt_reference", "wrong:receipt", ValueError), + ("calibration_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("target_population_digest", "a" * 63, ValueError), + ("analysis_window_reference", "wrong:window", ValueError), + ("auxiliary_projection_digest", "2" * 63, ValueError), + ("benchmark_receipt_digest", "7" * 65, ValueError), + ("algorithm_reference", "wrong:algorithm", ValueError), + ("algorithm_version", 0, ValueError), + ("constraints_digest", "9" * 63, ValueError), + ("termination_code", "failed", ValueError), + ("constructed_at", datetime(2026, 9, 16, 12, 0), ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "f" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(record, "termination_code", "fallback_applied") + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + CalibrationAdjustmentAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_view_rejects_tuple_base_constructor_forgery() -> None: + """Reject caller-authored instances created through the tuple base class.""" + with pytest.raises(TypeError): + tuple.__new__( + CalibrationAdjustmentAuthorityView, + (TENANT, STUDY, (("termination_code", "converged"),)), + ) + + +def test_raw_view_allocation_cannot_expose_projection_state() -> None: + """Keep an unissued raw allocation unusable through every public property.""" + forged = object.__new__(CalibrationAdjustmentAuthorityView) + + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _ = forged.tenant_record_id + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _ = forged.validity_study_id + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _ = forged.fields + + +def test_view_rejects_caller_authored_issuance_marker() -> None: + """Reject a raw allocation even when a caller invents a marker value.""" + forged = object.__new__(CalibrationAdjustmentAuthorityView) + object.__setattr__(forged, "_tenant_identity", TENANT) + object.__setattr__(forged, "_study_identity", STUDY) + object.__setattr__(forged, "_fields", ()) + object.__setattr__(forged, "_issuance_marker", object()) + + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _ = forged.fields + + +def test_importable_marker_cannot_mint_calibration_adjustment_view() -> None: + """Keep the resolver's view-sealing capability out of ordinary module state.""" + assert not hasattr( + authority_module, + "_CALIBRATION_ADJUSTMENT_VIEW_ISSUANCE_MARKER", + ) + forged = object.__new__(CalibrationAdjustmentAuthorityView) + object.__setattr__(forged, "_tenant_identity", TENANT.int) + object.__setattr__(forged, "_study_identity", STUDY.int) + object.__setattr__(forged, "_fields", (("termination_code", "converged"),)) + object.__setattr__( + forged, + "_issuance_marker", + getattr( + authority_module, + "_CALIBRATION_ADJUSTMENT_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _ = forged.fields + + +def test_issued_view_rejects_mutation_and_deletion() -> None: + """Keep a resolver-issued view immutable after all owner checks complete.""" + view = _resolve(read_port=_ReadPort(_record())) + + with pytest.raises(AttributeError): + view._fields = () + with pytest.raises(AttributeError): + del view._fields diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_calibration_adjustment_authority_structural_integrity.py new file mode 100644 index 000000000..d2f7d5a25 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_authority_structural_integrity.py @@ -0,0 +1,33 @@ +"""Structural-integrity regressions for calibration-adjustment owner evidence.""" + +from __future__ import annotations + +import pytest + +from orgmetra_workforce_validation_api.calibration_adjustment_authority import ( + CalibrationAdjustmentAuthorityIntegrityError, + CalibrationAdjustmentAuthorityRecord, +) +from test_calibration_adjustment_authority import _ReadPort, _record, _resolve + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + CalibrationAdjustmentAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + CalibrationAdjustmentAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_context_authority.py b/services/workforce-validation-api/tests/test_calibration_adjustment_context_authority.py new file mode 100644 index 000000000..9afda410e --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_context_authority.py @@ -0,0 +1,113 @@ +"""Regression contract for calibration target-population and analysis-window authority.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +from orgmetra_workforce_validation_api.calibration_adjustment_authority import ( + CalibrationAdjustmentAuthorityReadPort, + CalibrationAdjustmentAuthorityRecord, + resolve_calibration_adjustment_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +CONSTRUCTED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +TARGET_POPULATION_DIGEST = "a" * 64 +ANALYSIS_WINDOW_REFERENCE = "analysis_window:2026q3" + + +def _record() -> CalibrationAdjustmentAuthorityRecord: + """Build one released calibration authority with explicit analysis context.""" + return CalibrationAdjustmentAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + calibration_receipt_reference=( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + calibration_receipt_digest="1" * 64, + evidence_version=1, + target_population_digest=TARGET_POPULATION_DIGEST, + analysis_window_reference=ANALYSIS_WINDOW_REFERENCE, + auxiliary_authority_reference=( + "scientific_auxiliary_authority:33333333-3333-4333-8333-333333333333" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:44444444-4444-4444-8444-444444444444" + ), + auxiliary_projection_version=2, + auxiliary_projection_digest="2" * 64, + auxiliary_purpose_reference=( + "scientific_data_use_purpose:55555555-5555-4555-8555-555555555555" + ), + auxiliary_purpose_digest="3" * 64, + auxiliary_owner_contract_reference=( + "released_owner_contract:66666666-6666-4666-8666-666666666666" + ), + auxiliary_owner_contract_version=3, + auxiliary_owner_contract_digest="4" * 64, + auxiliary_authorization_receipt_reference=( + "scientific_data_authorization:77777777-7777-4777-8777-777777777777" + ), + auxiliary_authorization_receipt_digest="5" * 64, + auxiliary_scientific_use_receipt_reference=( + "scientific_use_receipt:88888888-8888-4888-8888-888888888888" + ), + auxiliary_scientific_use_receipt_digest="6" * 64, + auxiliary_scientific_use_at=datetime(2026, 9, 17, 7, 30, tzinfo=timezone.utc), + benchmark_receipt_reference=( + "calibration_benchmark_receipt:99999999-9999-4999-8999-999999999999" + ), + benchmark_receipt_version=4, + benchmark_receipt_digest="7" * 64, + benchmark_owner_contract_reference=( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + benchmark_owner_contract_version=5, + benchmark_owner_contract_digest="8" * 64, + benchmark_reference_at=datetime(2026, 9, 17, 7, 45, tzinfo=timezone.utc), + algorithm_reference="calibration_algorithm:linear-raking", + algorithm_version=1, + constraints_digest="9" * 64, + termination_code="converged", + input_weight_artifact_digest="b" * 64, + output_weight_artifact_digest="c" * 64, + constructed_at=CONSTRUCTED_AT, + fallback_reason_code=None, + fallback_rule_reference=None, + fallback_rule_digest=None, + fallback_algorithm_reference=None, + fallback_algorithm_version=None, + fallback_configuration_digest=None, + owner_contract_reference=( + "released_owner_contract:22222222-2222-4222-8222-222222222222" + ), + owner_contract_version=1, + owner_contract_digest="d" * 64, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def test_calibration_authority_binds_target_population_and_analysis_window() -> None: + """Keep the scientific leaf's population/window semantics in owner corroboration.""" + record = _record() + + assert record.target_population_digest == TARGET_POPULATION_DIGEST + assert record.analysis_window_reference == ANALYSIS_WINDOW_REFERENCE + + read_parameters = signature( + CalibrationAdjustmentAuthorityReadPort.read_calibration_adjustment_authority + ).parameters + resolver_parameters = signature(resolve_calibration_adjustment_authority).parameters + for field_name in ("target_population_digest", "analysis_window_reference"): + assert field_name in read_parameters + assert field_name in resolver_parameters + + for owner_resolved_field in ("owner_contract_released_at", "released_at"): + assert owner_resolved_field not in read_parameters + assert owner_resolved_field not in resolver_parameters diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_currentness.py b/services/workforce-validation-api/tests/test_calibration_adjustment_currentness.py new file mode 100644 index 000000000..d54e02791 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_currentness.py @@ -0,0 +1,168 @@ +"""Currentness contract for released typed calibration-adjustment evidence.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.calibration_adjustment_authority import ( + CalibrationAdjustmentAuthorityIntegrityError, + CalibrationAdjustmentAuthorityRecord, + _READ_FIELDS, + resolve_calibration_adjustment_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RELEASED = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 17, 13, 0, tzinfo=timezone.utc) + + +class _ReadPort: + def __init__(self, record: CalibrationAdjustmentAuthorityRecord) -> None: + self.record = record + + def read_calibration_adjustment_authority( + self, **_: object + ) -> CalibrationAdjustmentAuthorityRecord: + return self.record + + +def _record(**overrides: object) -> CalibrationAdjustmentAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111", + "calibration_receipt_digest": "1" * 64, + "evidence_version": 1, + "target_population_digest": "2" * 64, + "analysis_window_reference": "analysis_window:2026q3", + "auxiliary_authority_reference": "scientific_auxiliary_authority:33333333-3333-4333-8333-333333333333", + "auxiliary_projection_reference": "calibration_auxiliary_projection:44444444-4444-4444-8444-444444444444", + "auxiliary_projection_version": 1, + "auxiliary_projection_digest": "3" * 64, + "auxiliary_purpose_reference": "scientific_data_use_purpose:55555555-5555-4555-8555-555555555555", + "auxiliary_purpose_digest": "4" * 64, + "auxiliary_owner_contract_reference": "released_owner_contract:66666666-6666-4666-8666-666666666666", + "auxiliary_owner_contract_version": 1, + "auxiliary_owner_contract_digest": "5" * 64, + "auxiliary_authorization_receipt_reference": "scientific_data_authorization:77777777-7777-4777-8777-777777777777", + "auxiliary_authorization_receipt_digest": "6" * 64, + "auxiliary_scientific_use_receipt_reference": "scientific_use_receipt:88888888-8888-4888-8888-888888888888", + "auxiliary_scientific_use_receipt_digest": "7" * 64, + "auxiliary_scientific_use_at": datetime(2026, 9, 16, 11, 0, tzinfo=timezone.utc), + "benchmark_receipt_reference": "calibration_benchmark_receipt:99999999-9999-4999-8999-999999999999", + "benchmark_receipt_version": 1, + "benchmark_receipt_digest": "8" * 64, + "benchmark_owner_contract_reference": "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + "benchmark_owner_contract_version": 1, + "benchmark_owner_contract_digest": "9" * 64, + "benchmark_reference_at": datetime(2026, 9, 16, 11, 30, tzinfo=timezone.utc), + "algorithm_reference": "calibration_algorithm:generalized_regression", + "algorithm_version": 1, + "constraints_digest": "a" * 64, + "termination_code": "converged", + "input_weight_artifact_digest": "b" * 64, + "output_weight_artifact_digest": "c" * 64, + "constructed_at": datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc), + "fallback_reason_code": None, + "fallback_rule_reference": None, + "fallback_rule_digest": None, + "fallback_algorithm_reference": None, + "fallback_algorithm_version": None, + "fallback_configuration_digest": None, + "owner_contract_reference": "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", + "owner_contract_version": 1, + "owner_contract_digest": "d" * 64, + "owner_contract_released_at": datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc), + "released_at": RELEASED, + "superseded_at": CUTOVER, + } + values.update(overrides) + return CalibrationAdjustmentAuthorityRecord(**values) + + +def _resolve(record: CalibrationAdjustmentAuthorityRecord, *, used_at: datetime) -> None: + resolve_calibration_adjustment_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + calibration_receipt_reference=record.calibration_receipt_reference, + calibration_receipt_digest=record.calibration_receipt_digest, + evidence_version=record.evidence_version, + target_population_digest=record.target_population_digest, + analysis_window_reference=record.analysis_window_reference, + auxiliary_authority_reference=record.auxiliary_authority_reference, + auxiliary_projection_reference=record.auxiliary_projection_reference, + auxiliary_projection_version=record.auxiliary_projection_version, + auxiliary_projection_digest=record.auxiliary_projection_digest, + auxiliary_purpose_reference=record.auxiliary_purpose_reference, + auxiliary_purpose_digest=record.auxiliary_purpose_digest, + auxiliary_owner_contract_reference=record.auxiliary_owner_contract_reference, + auxiliary_owner_contract_version=record.auxiliary_owner_contract_version, + auxiliary_owner_contract_digest=record.auxiliary_owner_contract_digest, + auxiliary_authorization_receipt_reference=record.auxiliary_authorization_receipt_reference, + auxiliary_authorization_receipt_digest=record.auxiliary_authorization_receipt_digest, + auxiliary_scientific_use_receipt_reference=record.auxiliary_scientific_use_receipt_reference, + auxiliary_scientific_use_receipt_digest=record.auxiliary_scientific_use_receipt_digest, + auxiliary_scientific_use_at=record.auxiliary_scientific_use_at, + benchmark_receipt_reference=record.benchmark_receipt_reference, + benchmark_receipt_version=record.benchmark_receipt_version, + benchmark_receipt_digest=record.benchmark_receipt_digest, + benchmark_owner_contract_reference=record.benchmark_owner_contract_reference, + benchmark_owner_contract_version=record.benchmark_owner_contract_version, + benchmark_owner_contract_digest=record.benchmark_owner_contract_digest, + benchmark_reference_at=record.benchmark_reference_at, + algorithm_reference=record.algorithm_reference, + algorithm_version=record.algorithm_version, + constraints_digest=record.constraints_digest, + termination_code=record.termination_code, + input_weight_artifact_digest=record.input_weight_artifact_digest, + output_weight_artifact_digest=record.output_weight_artifact_digest, + constructed_at=record.constructed_at, + fallback_reason_code=record.fallback_reason_code, + fallback_rule_reference=record.fallback_rule_reference, + fallback_rule_digest=record.fallback_rule_digest, + fallback_algorithm_reference=record.fallback_algorithm_reference, + fallback_algorithm_version=record.fallback_algorithm_version, + fallback_configuration_digest=record.fallback_configuration_digest, + owner_contract_reference=record.owner_contract_reference, + owner_contract_version=record.owner_contract_version, + owner_contract_digest=record.owner_contract_digest, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-adjustment-authority-read-v1", + resource_kind="calibration_adjustment_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=_READ_FIELDS, + ), + read_port=_ReadPort(record), + ) + + +def test_supersession_cutover_is_owner_resolved_and_half_open() -> None: + record = _record() + _resolve(record, used_at=CUTOVER - timedelta(microseconds=1)) + + with pytest.raises(CalibrationAdjustmentAuthorityIntegrityError, match="superseded"): + _resolve(record, used_at=CUTOVER) + + +def test_superseded_at_requires_timezone_and_post_release_cutover() -> None: + with pytest.raises(ValueError): + _record(superseded_at=datetime(2026, 9, 17, 13, 0)) + + with pytest.raises(ValueError, match="later than released_at"): + _record(superseded_at=RELEASED) diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_owner_contract_chronology.py b/services/workforce-validation-api/tests/test_calibration_adjustment_owner_contract_chronology.py new file mode 100644 index 000000000..85a923215 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_owner_contract_chronology.py @@ -0,0 +1,121 @@ +"""Fail closed when calibration-adjustment owner contracts are retroactive.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.calibration_adjustment_authority import ( + CalibrationAdjustmentAuthorityRecord, + resolve_calibration_adjustment_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> CalibrationAdjustmentAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": ( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + "calibration_receipt_digest": "1" * 64, + "evidence_version": 1, + "target_population_digest": "a" * 64, + "analysis_window_reference": "analysis_window:2026q3", + "auxiliary_authority_reference": ( + "scientific_auxiliary_authority:44444444-4444-4444-8444-444444444444" + ), + "auxiliary_projection_reference": ( + "calibration_auxiliary_projection:55555555-5555-4555-8555-555555555555" + ), + "auxiliary_projection_version": 2, + "auxiliary_projection_digest": "2" * 64, + "auxiliary_purpose_reference": ( + "scientific_data_use_purpose:66666666-6666-4666-8666-666666666666" + ), + "auxiliary_purpose_digest": "8" * 64, + "auxiliary_owner_contract_reference": ( + "released_owner_contract:77777777-7777-4777-8777-777777777777" + ), + "auxiliary_owner_contract_version": 3, + "auxiliary_owner_contract_digest": "9" * 64, + "auxiliary_authorization_receipt_reference": ( + "scientific_data_authorization:88888888-8888-4888-8888-888888888888" + ), + "auxiliary_authorization_receipt_digest": "a" * 64, + "auxiliary_scientific_use_receipt_reference": ( + "scientific_use_receipt:99999999-9999-4999-8999-999999999999" + ), + "auxiliary_scientific_use_receipt_digest": "b" * 64, + "auxiliary_scientific_use_at": datetime( + 2026, 9, 16, 11, 0, tzinfo=timezone.utc + ), + "benchmark_receipt_reference": ( + "calibration_benchmark_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + "benchmark_receipt_version": 4, + "benchmark_receipt_digest": "3" * 64, + "benchmark_owner_contract_reference": ( + "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + ), + "benchmark_owner_contract_version": 4, + "benchmark_owner_contract_digest": "c" * 64, + "benchmark_reference_at": datetime(2026, 9, 16, 11, 30, tzinfo=timezone.utc), + "algorithm_reference": "calibration_algorithm:raking", + "algorithm_version": 2, + "constraints_digest": "4" * 64, + "termination_code": "converged", + "input_weight_artifact_digest": "5" * 64, + "output_weight_artifact_digest": "6" * 64, + "constructed_at": CONSTRUCTED_AT, + "fallback_reason_code": None, + "fallback_rule_reference": None, + "fallback_rule_digest": None, + "fallback_algorithm_reference": None, + "fallback_algorithm_version": None, + "fallback_configuration_digest": None, + "owner_contract_reference": ( + "released_owner_contract:33333333-3333-4333-8333-333333333333" + ), + "owner_contract_version": 5, + "owner_contract_digest": "7" * 64, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return CalibrationAdjustmentAuthorityRecord(**values) + + +def test_owner_contract_release_is_owner_resolved_not_a_request_coordinate() -> None: + assert "owner_contract_released_at" not in signature( + resolve_calibration_adjustment_authority + ).parameters + + +def test_owner_contract_must_exist_before_calibration_receipt_release() -> None: + with pytest.raises(ValueError, match="owner_contract_released_at"): + _record( + owner_contract_released_at=datetime( + 2026, 9, 16, 13, 0, 1, tzinfo=timezone.utc + ) + ) + + +def test_owner_contract_release_requires_timezone_aware_evidence() -> None: + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 16, 12, 30)) + + +def test_contract_released_after_construction_but_before_receipt_release_is_valid() -> None: + record = _record() + assert record.owner_contract_released_at == OWNER_CONTRACT_RELEASED_AT + assert record.released_at == RELEASED_AT diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..cc6726854 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for calibration-adjustment supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.calibration_adjustment_supersession_authority import ( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + CalibrationAdjustmentSupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_low_level_tuple_construction_cannot_issue_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + CalibrationAdjustmentSupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("calibration_receipt_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(CalibrationAdjustmentSupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + match="was not issued by resolve_calibration_adjustment_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(CalibrationAdjustmentSupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + match="was not issued by resolve_calibration_adjustment_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(CalibrationAdjustmentSupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..1c4aad7a5 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_authority_view_seal_capability.py @@ -0,0 +1,58 @@ +"""Hostile sealing-capability regression for calibration supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.calibration_adjustment_supersession_authority as authority_module +from orgmetra_workforce_validation_api.calibration_adjustment_supersession_authority import ( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + CalibrationAdjustmentSupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000421") +STUDY = UUID("00000000-0000-0000-0000-000000000422") + + +def _raw_view_with_module_marker() -> CalibrationAdjustmentSupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(CalibrationAdjustmentSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__( + view, + "_fields", + (("calibration_receipt_digest", "a" * 64),), + ) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_CALIBRATION_ADJUSTMENT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_calibration_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_CALIBRATION_ADJUSTMENT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_calibration_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + match=( + "calibration-adjustment supersession view was not issued by " + "resolve_calibration_adjustment_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_contract.py b/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_contract.py new file mode 100644 index 000000000..651023008 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_supersession_contract.py @@ -0,0 +1,351 @@ +"""Append-only correction contract for typed calibration-adjustment authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.calibration_adjustment_supersession_authority import ( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + CalibrationAdjustmentSupersessionAuthorityNotFound, + CalibrationAdjustmentSupersessionAuthorityReadPort, + CalibrationAdjustmentSupersessionAuthorityRecord, + CalibrationAdjustmentSupersessionAuthorityView, + resolve_calibration_adjustment_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT = "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR = "calibration_adjustment_receipt:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "2" * 64 +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 17, 13, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "calibration_receipt_reference", + "calibration_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_calibration_receipt_reference", + "successor_calibration_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured correction evidence and retain exact lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_calibration_adjustment_supersession_authority( + self, **coordinates: object + ) -> object: + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately omit the owner read capability.""" + + +class _ProtocolOnly(CalibrationAdjustmentSupersessionAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must be rejected without execution.""" + + @property + def read_calibration_adjustment_supersession_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-adjustment-supersession-read-v1", + resource_kind="calibration_adjustment_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> CalibrationAdjustmentSupersessionAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": RECEIPT, + "calibration_receipt_digest": DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED, + "released_at": RELEASED, + "superseded_at": CUTOVER, + "successor_calibration_receipt_reference": SUCCESSOR, + "successor_calibration_receipt_digest": SUCCESSOR_DIGEST, + "successor_evidence_version": 1, + "successor_released_at": CUTOVER, + } + values.update(overrides) + return CalibrationAdjustmentSupersessionAuthorityRecord(**values) + + +def _resolve(*, read_port: object, used_at: datetime, **overrides: object): + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": RECEIPT, + "calibration_receipt_digest": DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "used_at": used_at, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_calibration_adjustment_supersession_authority(**values) + + +def test_successor_edge_requires_complete_atomic_released_coordinates() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(ValueError, match="complete released successor coordinates"): + _record(successor_released_at=None) + with pytest.raises(ValueError, match="later than calibration receipt release"): + _record(superseded_at=RELEASED) + with pytest.raises(ValueError, match="new reference"): + _record(successor_calibration_receipt_reference=RECEIPT) + with pytest.raises(ValueError, match="new evidence"): + _record(successor_calibration_receipt_digest=DIGEST) + with pytest.raises(ValueError, match="successor_evidence_version must remain 1"): + _record(successor_evidence_version=2) + with pytest.raises(ValueError, match="released after its predecessor"): + _record(superseded_at=RELEASED + timedelta(seconds=1), successor_released_at=RELEASED) + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=CUTOVER - timedelta(seconds=1)) + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=CUTOVER + timedelta(seconds=1)) + + +def test_chronology_requires_released_owner_and_timezone_aware_instants() -> None: + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 16, 12, 30)) + with pytest.raises(ValueError): + _record(released_at=datetime(2026, 9, 16, 13, 0)) + with pytest.raises(ValueError): + _record(superseded_at=datetime(2026, 9, 17, 13, 0)) + with pytest.raises(ValueError): + _record(successor_released_at=datetime(2026, 9, 17, 13, 0)) + + +def test_historical_use_is_allowed_but_cutover_use_fails_closed() -> None: + record = _record() + port = _ReadPort(record) + view = _resolve(read_port=port, used_at=CUTOVER - timedelta(microseconds=1)) + + assert view.validity_study_id == STUDY + + assert isinstance(port, CalibrationAdjustmentSupersessionAuthorityReadPort) + assert port.calls == [ + { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": RECEIPT, + "calibration_receipt_digest": DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + } + ] + assert ("calibration_receipt_reference", RECEIPT) in view.fields + assert ("released_at", RELEASED) in view.fields + assert ("superseded_at", CUTOVER) in view.fields + assert all(not name.startswith("successor_") for name, _ in view.fields) + + with pytest.raises( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + match="superseded", + ): + _resolve(read_port=_ReadPort(record), used_at=CUTOVER) + + +def test_open_interval_without_successor_remains_current() -> None: + record = _record( + superseded_at=None, + successor_calibration_receipt_reference=None, + successor_calibration_receipt_digest=None, + successor_evidence_version=None, + successor_released_at=None, + ) + + view = _resolve(read_port=_ReadPort(record), used_at=CUTOVER + timedelta(days=30)) + assert ("released_at", RELEASED) in view.fields + assert ("superseded_at", None) in view.fields + + +def test_missing_noncanonical_and_pre_release_owner_evidence_fail_closed() -> None: + with pytest.raises(CalibrationAdjustmentSupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None), used_at=CUTOVER - timedelta(seconds=1)) + with pytest.raises(CalibrationAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object()), used_at=CUTOVER - timedelta(seconds=1)) + with pytest.raises( + CalibrationAdjustmentSupersessionAuthorityIntegrityError, + match="released before scientific use", + ): + _resolve(read_port=_ReadPort(_record()), used_at=RELEASED - timedelta(seconds=1)) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"calibration_receipt_reference": SUCCESSOR}, + {"calibration_receipt_digest": "4" * 64}, + {"owner_contract_reference": "released_owner_contract:44444444-4444-4444-8444-444444444444"}, + {"owner_contract_version": 2}, + {"owner_contract_digest": "5" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + if "calibration_receipt_reference" in record_overrides: + record_overrides = { + **record_overrides, + "successor_calibration_receipt_reference": RECEIPT, + } + record = _record(**record_overrides) + with pytest.raises(CalibrationAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(record), used_at=CUTOVER - timedelta(seconds=1)) + + +def test_authorization_denial_precedes_owner_read() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve( + read_port=port, + used_at=CUTOVER - timedelta(seconds=1), + policy=_policy(purpose_code="audit_review"), + ) + assert port.calls == [] + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("calibration_receipt_reference", "wrong:receipt", ValueError), + ("calibration_receipt_digest", "ABC", ValueError), + ("evidence_version", 2, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "3" * 63, ValueError), + ("used_at", datetime(2026, 9, 17, 12, 0), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve( + read_port=port, + **{"used_at": CUTOVER - timedelta(seconds=1), **overrides}, + ) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_immutable_and_uuid_views_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(record, "released_at", CUTOVER) + + view = _resolve(read_port=_ReadPort(record), used_at=CUTOVER - timedelta(seconds=1)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + + with pytest.raises(TypeError): + CalibrationAdjustmentSupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_exact_typed_hidden_tail_record_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + CalibrationAdjustmentSupersessionAuthorityRecord, + tuple(canonical) + (("hidden_owner_coordinate", "must-not-normalize-away"),), + ) + + with pytest.raises(CalibrationAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged), used_at=RELEASED) + + +def test_exact_typed_truncated_record_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + CalibrationAdjustmentSupersessionAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(CalibrationAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged), used_at=RELEASED) diff --git a/services/workforce-validation-api/tests/test_calibration_adjustment_supporting_authority_identity.py b/services/workforce-validation-api/tests/test_calibration_adjustment_supporting_authority_identity.py new file mode 100644 index 000000000..72a6d3cd1 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_adjustment_supporting_authority_identity.py @@ -0,0 +1,131 @@ +"""Regression contract for exact auxiliary and benchmark identity in calibration evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +from orgmetra_workforce_validation_api.calibration_adjustment_authority import ( + CalibrationAdjustmentAuthorityReadPort, + CalibrationAdjustmentAuthorityRecord, + resolve_calibration_adjustment_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +CONSTRUCTED_AT = datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 9, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 9, 30, tzinfo=timezone.utc) +SUPPORTING_FIELDS = ( + "auxiliary_authority_reference", + "auxiliary_projection_reference", + "auxiliary_projection_version", + "auxiliary_purpose_reference", + "auxiliary_purpose_digest", + "auxiliary_owner_contract_reference", + "auxiliary_owner_contract_version", + "auxiliary_owner_contract_digest", + "auxiliary_authorization_receipt_reference", + "auxiliary_authorization_receipt_digest", + "auxiliary_scientific_use_receipt_reference", + "auxiliary_scientific_use_receipt_digest", + "auxiliary_scientific_use_at", + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_reference_at", +) + + +def _record() -> CalibrationAdjustmentAuthorityRecord: + """Build the complete leaf-semantic calibration authority projection.""" + return CalibrationAdjustmentAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + calibration_receipt_reference=( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + calibration_receipt_digest="1" * 64, + evidence_version=1, + target_population_digest="2" * 64, + analysis_window_reference="analysis_window:2026q3", + auxiliary_authority_reference=( + "scientific_auxiliary_authority:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:33333333-3333-4333-8333-333333333333" + ), + auxiliary_projection_version=3, + auxiliary_projection_digest="3" * 64, + auxiliary_purpose_reference=( + "scientific_data_use_purpose:44444444-4444-4444-8444-444444444444" + ), + auxiliary_purpose_digest="4" * 64, + auxiliary_owner_contract_reference=( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + auxiliary_owner_contract_version=5, + auxiliary_owner_contract_digest="5" * 64, + auxiliary_authorization_receipt_reference=( + "scientific_data_authorization:66666666-6666-4666-8666-666666666666" + ), + auxiliary_authorization_receipt_digest="6" * 64, + auxiliary_scientific_use_receipt_reference=( + "scientific_use_receipt:77777777-7777-4777-8777-777777777777" + ), + auxiliary_scientific_use_receipt_digest="7" * 64, + auxiliary_scientific_use_at=datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc), + benchmark_receipt_reference=( + "calibration_benchmark_receipt:88888888-8888-4888-8888-888888888888" + ), + benchmark_receipt_version=8, + benchmark_receipt_digest="8" * 64, + benchmark_owner_contract_reference=( + "released_owner_contract:99999999-9999-4999-8999-999999999999" + ), + benchmark_owner_contract_version=9, + benchmark_owner_contract_digest="9" * 64, + benchmark_reference_at=datetime(2026, 9, 17, 8, 45, tzinfo=timezone.utc), + algorithm_reference="calibration_algorithm:generalized_regression", + algorithm_version=3, + constraints_digest="a" * 64, + termination_code="converged", + input_weight_artifact_digest="b" * 64, + output_weight_artifact_digest="c" * 64, + constructed_at=CONSTRUCTED_AT, + fallback_reason_code=None, + fallback_rule_reference=None, + fallback_rule_digest=None, + fallback_algorithm_reference=None, + fallback_algorithm_version=None, + fallback_configuration_digest=None, + owner_contract_reference=( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + owner_contract_version=10, + owner_contract_digest="d" * 64, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def test_calibration_authority_preserves_exact_supporting_evidence_identity() -> None: + """Do not collapse calibration auxiliary/benchmark provenance to digest-only labels.""" + record = _record() + read_parameters = signature( + CalibrationAdjustmentAuthorityReadPort.read_calibration_adjustment_authority + ).parameters + resolver_parameters = signature(resolve_calibration_adjustment_authority).parameters + + for field_name in SUPPORTING_FIELDS: + assert hasattr(record, field_name) + assert field_name in read_parameters + assert field_name in resolver_parameters + + assert record.auxiliary_projection_reference.startswith("calibration_auxiliary_projection:") + assert record.benchmark_receipt_reference.startswith("calibration_benchmark_receipt:") + assert record.auxiliary_scientific_use_at <= record.constructed_at + assert record.benchmark_reference_at <= record.constructed_at diff --git a/services/workforce-validation-api/tests/test_calibration_auxiliary_authority.py b/services/workforce-validation-api/tests/test_calibration_auxiliary_authority.py new file mode 100644 index 000000000..7f5baaf61 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_auxiliary_authority.py @@ -0,0 +1,534 @@ +"""Fail-closed contract for resolving calibration auxiliary-use authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.scientific_authority as authority_module +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.scientific_authority import ( + CalibrationAuxiliaryAuthorityIntegrityError, + CalibrationAuxiliaryAuthorityNotFound, + CalibrationAuxiliaryAuthorityReadPort, + CalibrationAuxiliaryAuthorityRecord, + CalibrationAuxiliaryAuthorityView, + resolve_calibration_auxiliary_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000c2") +AUTHORITY_REFERENCE = ( + "scientific_auxiliary_authority:11111111-1111-4111-8111-111111111111" +) +PROJECTION_REFERENCE = ( + "calibration_auxiliary_projection:22222222-2222-4222-8222-222222222222" +) +PROJECTION_VERSION = 4 +PURPOSE_REFERENCE = ( + "scientific_data_use_purpose:33333333-3333-4333-8333-333333333333" +) +OWNER_CONTRACT_REFERENCE = ( + "released_owner_contract:44444444-4444-4444-8444-444444444444" +) +AUTHORIZATION_REFERENCE = ( + "scientific_data_authorization:55555555-5555-4555-8555-555555555555" +) +SCIENTIFIC_USE_REFERENCE = ( + "scientific_use_receipt:66666666-6666-4666-8666-666666666666" +) +PROJECTION_DIGEST = "1" * 64 +PURPOSE_DIGEST = "2" * 64 +OWNER_CONTRACT_DIGEST = "3" * 64 +AUTHORIZATION_DIGEST = "4" * 64 +SCIENTIFIC_USE_DIGEST = "5" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 8, 31, tzinfo=timezone.utc) +AUTHORIZATION_RECEIPT_RELEASED_AT = datetime(2026, 8, 31, 12, tzinfo=timezone.utc) +AUTHORIZED_FROM = datetime(2026, 9, 1, tzinfo=timezone.utc) +AUTHORIZED_TO = datetime(2026, 10, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "authority_reference", + "auxiliary_projection_reference", + "auxiliary_projection_version", + "auxiliary_projection_digest", + "scientific_purpose_reference", + "scientific_purpose_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "authorization_receipt_reference", + "authorization_receipt_digest", + "authorization_receipt_released_at", + "scientific_use_receipt_reference", + "scientific_use_receipt_digest", + "scientific_use_at", + "authorized_from", + "authorized_to", + } +) + + +class _ReadPort: + """Return one configured authority record and retain the exact lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[tuple[object, ...]] = [] + + def read_calibration_auxiliary_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + authority_reference: str, + auxiliary_projection_reference: str, + auxiliary_projection_version: int, + auxiliary_projection_digest: str, + scientific_purpose_reference: str, + scientific_purpose_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + authorization_receipt_reference: str, + authorization_receipt_digest: str, + scientific_use_receipt_reference: str, + scientific_use_receipt_digest: str, + ) -> object: + """Capture the owner lookup and return the configured result.""" + self.calls.append( + ( + tenant_record_id, + validity_study_id, + authority_reference, + auxiliary_projection_reference, + auxiliary_projection_version, + auxiliary_projection_digest, + scientific_purpose_reference, + scientific_purpose_digest, + owner_contract_reference, + owner_contract_version, + owner_contract_digest, + authorization_receipt_reference, + authorization_receipt_digest, + scientific_use_receipt_reference, + scientific_use_receipt_digest, + ) + ) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(CalibrationAuxiliaryAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must be rejected without executing it.""" + + @property + def read_calibration_auxiliary_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-authority-read-v1", + resource_kind="calibration_auxiliary_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> CalibrationAuxiliaryAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY_REFERENCE, + "auxiliary_projection_reference": PROJECTION_REFERENCE, + "auxiliary_projection_version": PROJECTION_VERSION, + "auxiliary_projection_digest": PROJECTION_DIGEST, + "scientific_purpose_reference": PURPOSE_REFERENCE, + "scientific_purpose_digest": PURPOSE_DIGEST, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "authorization_receipt_reference": AUTHORIZATION_REFERENCE, + "authorization_receipt_digest": AUTHORIZATION_DIGEST, + "authorization_receipt_released_at": AUTHORIZATION_RECEIPT_RELEASED_AT, + "scientific_use_receipt_reference": SCIENTIFIC_USE_REFERENCE, + "scientific_use_receipt_digest": SCIENTIFIC_USE_DIGEST, + "scientific_use_at": USED_AT, + "authorized_from": AUTHORIZED_FROM, + "authorized_to": AUTHORIZED_TO, + } + values.update(overrides) + return CalibrationAuxiliaryAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> CalibrationAuxiliaryAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY_REFERENCE, + "auxiliary_projection_reference": PROJECTION_REFERENCE, + "auxiliary_projection_version": PROJECTION_VERSION, + "auxiliary_projection_digest": PROJECTION_DIGEST, + "scientific_purpose_reference": PURPOSE_REFERENCE, + "scientific_purpose_digest": PURPOSE_DIGEST, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "authorization_receipt_reference": AUTHORIZATION_REFERENCE, + "authorization_receipt_digest": AUTHORIZATION_DIGEST, + "scientific_use_receipt_reference": SCIENTIFIC_USE_REFERENCE, + "scientific_use_receipt_digest": SCIENTIFIC_USE_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_calibration_auxiliary_authority(**values) + + +def test_resolution_authorizes_then_returns_minimized_corroborated_evidence() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, CalibrationAuxiliaryAuthorityReadPort) + assert port.calls == [ + ( + TENANT, + STUDY, + AUTHORITY_REFERENCE, + PROJECTION_REFERENCE, + PROJECTION_VERSION, + PROJECTION_DIGEST, + PURPOSE_REFERENCE, + PURPOSE_DIGEST, + OWNER_CONTRACT_REFERENCE, + 7, + OWNER_CONTRACT_DIGEST, + AUTHORIZATION_REFERENCE, + AUTHORIZATION_DIGEST, + SCIENTIFIC_USE_REFERENCE, + SCIENTIFIC_USE_DIGEST, + ) + ] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert view.fields == ( + ("authority_reference", AUTHORITY_REFERENCE), + ("authorization_receipt_digest", AUTHORIZATION_DIGEST), + ("authorization_receipt_reference", AUTHORIZATION_REFERENCE), + ("authorization_receipt_released_at", AUTHORIZATION_RECEIPT_RELEASED_AT), + ("authorized_from", AUTHORIZED_FROM), + ("authorized_to", AUTHORIZED_TO), + ("auxiliary_projection_digest", PROJECTION_DIGEST), + ("auxiliary_projection_reference", PROJECTION_REFERENCE), + ("auxiliary_projection_version", PROJECTION_VERSION), + ("owner_contract_digest", OWNER_CONTRACT_DIGEST), + ("owner_contract_reference", OWNER_CONTRACT_REFERENCE), + ("owner_contract_released_at", OWNER_CONTRACT_RELEASED_AT), + ("owner_contract_version", 7), + ("scientific_purpose_digest", PURPOSE_DIGEST), + ("scientific_purpose_reference", PURPOSE_REFERENCE), + ("scientific_use_at", USED_AT), + ("scientific_use_receipt_digest", SCIENTIFIC_USE_DIGEST), + ("scientific_use_receipt_reference", SCIENTIFIC_USE_REFERENCE), + ) + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(CalibrationAuxiliaryAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + + with pytest.raises(CalibrationAuxiliaryAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + ("record_overrides", "request_overrides"), + [ + ({"tenant_record_id": OTHER_TENANT}, {}), + ({"validity_study_id": OTHER_STUDY}, {}), + ( + { + "authority_reference": ( + "scientific_auxiliary_authority:dddddddd-dddd-4ddd-8ddd-dddddddddddd" + ) + }, + {}, + ), + ( + { + "auxiliary_projection_reference": ( + "calibration_auxiliary_projection:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ) + }, + {}, + ), + ({"auxiliary_projection_version": PROJECTION_VERSION + 1}, {}), + ({"auxiliary_projection_digest": "a" * 64}, {}), + ( + { + "scientific_purpose_reference": ( + "scientific_data_use_purpose:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + ) + }, + {}, + ), + ({"scientific_purpose_digest": "b" * 64}, {}), + ( + { + "owner_contract_reference": ( + "released_owner_contract:cccccccc-cccc-4ccc-8ccc-cccccccccccc" + ) + }, + {}, + ), + ({"owner_contract_version": 8}, {}), + ({"owner_contract_digest": "c" * 64}, {}), + ( + { + "authorization_receipt_reference": ( + "scientific_data_authorization:eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee" + ) + }, + {}, + ), + ({"authorization_receipt_digest": "c" * 64}, {}), + ( + { + "scientific_use_receipt_reference": ( + "scientific_use_receipt:ffffffff-ffff-4fff-8fff-ffffffffffff" + ) + }, + {}, + ), + ({"scientific_use_receipt_digest": "d" * 64}, {}), + ({}, {"used_at": USED_AT + timedelta(seconds=1)}), + ], +) +def test_resolved_authority_must_match_every_requested_coordinate_and_use_time( + record_overrides: dict[str, object], request_overrides: dict[str, object] +) -> None: + with pytest.raises(CalibrationAuxiliaryAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides)), **request_overrides) + + +def test_record_requires_owner_resolved_use_time_inside_authorization_interval() -> None: + for invalid_use in ( + AUTHORIZED_FROM - timedelta(seconds=1), + AUTHORIZED_TO, + ): + with pytest.raises(ValueError): + _record(scientific_use_at=invalid_use) + + +def test_open_ended_authority_interval_accepts_later_owner_resolved_use() -> None: + later_use = AUTHORIZED_TO + timedelta(days=30) + view = _resolve( + read_port=_ReadPort( + _record( + scientific_use_at=later_use, + authorized_to=None, + ) + ), + used_at=later_use, + ) + assert dict(view.fields)["authorized_to"] is None + assert dict(view.fields)["scientific_use_at"] == later_use + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("authority_reference", "wrong:authority", ValueError), + ("auxiliary_projection_reference", "wrong:projection", ValueError), + ("auxiliary_projection_version", 0, ValueError), + ("auxiliary_projection_version", True, ValueError), + ("auxiliary_projection_digest", "ABC", ValueError), + ("scientific_purpose_reference", "wrong:purpose", ValueError), + ("scientific_purpose_digest", "2" * 63, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", True, ValueError), + ("owner_contract_digest", "3" * 63, ValueError), + ("authorization_receipt_reference", "wrong:authorization", ValueError), + ("authorization_receipt_digest", "4" * 65, ValueError), + ("scientific_use_receipt_reference", "wrong:use", ValueError), + ("scientific_use_receipt_digest", "5" * 65, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value # type: ignore[assignment] + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +@pytest.mark.parametrize( + ("key", "value"), + [ + ("tenant_record_id", UUID(int=0)), + ("validity_study_id", "not-a-uuid"), + ("authority_reference", "wrong:authority"), + ("auxiliary_projection_reference", "wrong:projection"), + ("auxiliary_projection_version", 0), + ("auxiliary_projection_version", True), + ("auxiliary_projection_digest", "1" * 63), + ("scientific_purpose_reference", "wrong:purpose"), + ("scientific_purpose_digest", "2" * 65), + ("owner_contract_reference", "wrong:contract"), + ("owner_contract_version", 0), + ("owner_contract_digest", "3" * 63), + ("owner_contract_released_at", datetime(2026, 8, 31)), + ("authorization_receipt_reference", "wrong:authorization"), + ("authorization_receipt_digest", "4" * 63), + ("authorization_receipt_released_at", datetime(2026, 8, 31)), + ("scientific_use_receipt_reference", "wrong:use"), + ("scientific_use_receipt_digest", "5" * 63), + ("scientific_use_at", datetime(2026, 9, 17)), + ("authorized_from", datetime(2026, 9, 1)), + ("authorized_to", "not-a-datetime"), + ], +) +def test_record_rejects_invalid_authority_evidence(key: str, value: object) -> None: + with pytest.raises(ValueError): + _record(**{key: value}) + + +def test_record_rejects_empty_or_reversed_authorization_interval() -> None: + for invalid_end in (AUTHORIZED_FROM, AUTHORIZED_FROM - timedelta(seconds=1)): + with pytest.raises(ValueError): + _record(authorized_to=invalid_end) + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(record, "owner_contract_version", 999) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + CalibrationAuxiliaryAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_low_level_view_allocation_cannot_expose_caller_authored_projection() -> None: + """Reject exact-runtime views that bypass the authorized resolver path.""" + with pytest.raises((TypeError, CalibrationAuxiliaryAuthorityIntegrityError)): + forged_tuple = tuple.__new__( + CalibrationAuxiliaryAuthorityView, + (TENANT.int, STUDY.int, (("authority_reference", AUTHORITY_REFERENCE),)), + ) + _ = forged_tuple.tenant_record_id + + with pytest.raises((TypeError, CalibrationAuxiliaryAuthorityIntegrityError)): + raw_view = object.__new__(CalibrationAuxiliaryAuthorityView) + _ = raw_view.fields + + wrong_marker_view = object.__new__(CalibrationAuxiliaryAuthorityView) + object.__setattr__(wrong_marker_view, "_issuance_marker", object()) + with pytest.raises(CalibrationAuxiliaryAuthorityIntegrityError): + _ = wrong_marker_view.validity_study_id + + +def test_importable_marker_cannot_mint_calibration_auxiliary_view() -> None: + """Keep auxiliary view-sealing authority out of ordinary module state.""" + assert not hasattr( + authority_module, + "_CALIBRATION_AUXILIARY_VIEW_ISSUANCE_MARKER", + ) + forged_view = object.__new__(CalibrationAuxiliaryAuthorityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__( + forged_view, + "_fields", + (("authority_reference", AUTHORITY_REFERENCE),), + ) + object.__setattr__( + forged_view, + "_issuance_marker", + getattr( + authority_module, + "_CALIBRATION_AUXILIARY_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + + with pytest.raises(CalibrationAuxiliaryAuthorityIntegrityError): + _ = forged_view.fields + + +def test_issued_view_rejects_attribute_deletion() -> None: + """Keep authorized projection state immutable after resolver issuance.""" + view = _resolve(read_port=_ReadPort(_record())) + with pytest.raises(AttributeError, match="immutable"): + view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del view._fields diff --git a/services/workforce-validation-api/tests/test_calibration_auxiliary_authority_chronology.py b/services/workforce-validation-api/tests/test_calibration_auxiliary_authority_chronology.py new file mode 100644 index 000000000..d57fa72f0 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_auxiliary_authority_chronology.py @@ -0,0 +1,78 @@ +"""Chronology contract for calibration auxiliary-use authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.scientific_authority import ( + CalibrationAuxiliaryAuthorityRecord, + resolve_calibration_auxiliary_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") +AUTHORIZED_FROM = datetime(2026, 9, 1, tzinfo=timezone.utc) +AUTHORIZED_TO = datetime(2026, 10, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = AUTHORIZED_FROM - timedelta(days=1) +AUTHORIZATION_RECEIPT_RELEASED_AT = AUTHORIZED_FROM - timedelta(hours=12) + + +def _record(*, owner_contract_released_at: object) -> CalibrationAuxiliaryAuthorityRecord: + return CalibrationAuxiliaryAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + authority_reference=( + "scientific_auxiliary_authority:11111111-1111-4111-8111-111111111111" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_version=4, + auxiliary_projection_digest="1" * 64, + scientific_purpose_reference=( + "scientific_data_use_purpose:33333333-3333-4333-8333-333333333333" + ), + scientific_purpose_digest="2" * 64, + owner_contract_reference=( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + owner_contract_version=7, + owner_contract_digest="3" * 64, + owner_contract_released_at=owner_contract_released_at, + authorization_receipt_reference=( + "scientific_data_authorization:55555555-5555-4555-8555-555555555555" + ), + authorization_receipt_digest="4" * 64, + authorization_receipt_released_at=AUTHORIZATION_RECEIPT_RELEASED_AT, + scientific_use_receipt_reference=( + "scientific_use_receipt:66666666-6666-4666-8666-666666666666" + ), + scientific_use_receipt_digest="5" * 64, + scientific_use_at=USED_AT, + authorized_from=AUTHORIZED_FROM, + authorized_to=AUTHORIZED_TO, + ) + + +def test_owner_contract_release_is_owner_evidence_not_a_caller_coordinate() -> None: + assert "owner_contract_released_at" not in signature( + resolve_calibration_auxiliary_authority + ).parameters + + record = _record(owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT) + assert record.owner_contract_released_at == OWNER_CONTRACT_RELEASED_AT + + +def test_owner_contract_cannot_retroactively_authorize_the_interval() -> None: + with pytest.raises(ValueError, match="owner contract must be released no later"): + _record(owner_contract_released_at=AUTHORIZED_FROM + timedelta(seconds=1)) + + +def test_owner_contract_release_requires_timezone_aware_evidence() -> None: + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 8, 31)) diff --git a/services/workforce-validation-api/tests/test_calibration_auxiliary_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_calibration_auxiliary_authority_structural_integrity.py new file mode 100644 index 000000000..cfac62e3a --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_auxiliary_authority_structural_integrity.py @@ -0,0 +1,141 @@ +"""Structural-integrity regressions for calibration auxiliary owner evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +import orgmetra_workforce_validation_api.scientific_authority as target +from orgmetra_workforce_validation_api.scientific_authority import ( + CalibrationAuxiliaryAuthorityIntegrityError, + CalibrationAuxiliaryAuthorityRecord, + resolve_calibration_auxiliary_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 8, 31, tzinfo=timezone.utc) +AUTHORIZATION_RECEIPT_RELEASED_AT = datetime(2026, 8, 31, 12, tzinfo=timezone.utc) +AUTHORIZED_FROM = datetime(2026, 9, 1, tzinfo=timezone.utc) +AUTHORIZED_TO = datetime(2026, 10, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) + + +class _ReadPort: + """Return configured persisted evidence through the auxiliary owner capability.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_calibration_auxiliary_authority(self, **_: object) -> object: + """Return the configured owner evidence.""" + return self.result + + +def _record() -> CalibrationAuxiliaryAuthorityRecord: + return CalibrationAuxiliaryAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + authority_reference=( + "scientific_auxiliary_authority:11111111-1111-4111-8111-111111111111" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_version=4, + auxiliary_projection_digest="1" * 64, + scientific_purpose_reference=( + "scientific_data_use_purpose:33333333-3333-4333-8333-333333333333" + ), + scientific_purpose_digest="2" * 64, + owner_contract_reference=( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + owner_contract_version=7, + owner_contract_digest="3" * 64, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + authorization_receipt_reference=( + "scientific_data_authorization:55555555-5555-4555-8555-555555555555" + ), + authorization_receipt_digest="4" * 64, + authorization_receipt_released_at=AUTHORIZATION_RECEIPT_RELEASED_AT, + scientific_use_receipt_reference=( + "scientific_use_receipt:66666666-6666-4666-8666-666666666666" + ), + scientific_use_receipt_digest="5" * 64, + scientific_use_at=USED_AT, + authorized_from=AUTHORIZED_FROM, + authorized_to=AUTHORIZED_TO, + ) + + +def _resolve(result: object) -> object: + return resolve_calibration_auxiliary_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + authority_reference=( + "scientific_auxiliary_authority:11111111-1111-4111-8111-111111111111" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_version=4, + auxiliary_projection_digest="1" * 64, + scientific_purpose_reference=( + "scientific_data_use_purpose:33333333-3333-4333-8333-333333333333" + ), + scientific_purpose_digest="2" * 64, + owner_contract_reference=( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + owner_contract_version=7, + owner_contract_digest="3" * 64, + authorization_receipt_reference=( + "scientific_data_authorization:55555555-5555-4555-8555-555555555555" + ), + authorization_receipt_digest="4" * 64, + scientific_use_receipt_reference=( + "scientific_use_receipt:66666666-6666-4666-8666-666666666666" + ), + scientific_use_receipt_digest="5" * 64, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-authority-read-v1", + resource_kind="calibration_auxiliary_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=target._READ_FIELDS, + ), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + CalibrationAuxiliaryAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(CalibrationAuxiliaryAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__(CalibrationAuxiliaryAuthorityRecord, tuple(canonical)[:-1]) + + with pytest.raises(CalibrationAuxiliaryAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_calibration_auxiliary_authorization_receipt_chronology.py b/services/workforce-validation-api/tests/test_calibration_auxiliary_authorization_receipt_chronology.py new file mode 100644 index 000000000..0b61f91f1 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_auxiliary_authorization_receipt_chronology.py @@ -0,0 +1,93 @@ +"""Chronology contract for calibration auxiliary authorization receipts.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.scientific_authority import ( + CalibrationAuxiliaryAuthorityRecord, + resolve_calibration_auxiliary_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 8, 30, tzinfo=timezone.utc) +AUTHORIZATION_RECEIPT_RELEASED_AT = datetime(2026, 8, 31, tzinfo=timezone.utc) +AUTHORIZED_FROM = datetime(2026, 9, 1, tzinfo=timezone.utc) +AUTHORIZED_TO = datetime(2026, 10, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) + + +def _record(*, authorization_receipt_released_at: object) -> CalibrationAuxiliaryAuthorityRecord: + return CalibrationAuxiliaryAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + authority_reference=( + "scientific_auxiliary_authority:11111111-1111-4111-8111-111111111111" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_version=4, + auxiliary_projection_digest="1" * 64, + scientific_purpose_reference=( + "scientific_data_use_purpose:33333333-3333-4333-8333-333333333333" + ), + scientific_purpose_digest="2" * 64, + owner_contract_reference=( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + owner_contract_version=7, + owner_contract_digest="3" * 64, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + authorization_receipt_reference=( + "scientific_data_authorization:55555555-5555-4555-8555-555555555555" + ), + authorization_receipt_digest="4" * 64, + authorization_receipt_released_at=authorization_receipt_released_at, + scientific_use_receipt_reference=( + "scientific_use_receipt:66666666-6666-4666-8666-666666666666" + ), + scientific_use_receipt_digest="5" * 64, + scientific_use_at=USED_AT, + authorized_from=AUTHORIZED_FROM, + authorized_to=AUTHORIZED_TO, + ) + + +def test_authorization_receipt_release_is_owner_evidence_not_a_caller_coordinate() -> None: + assert "authorization_receipt_released_at" not in signature( + resolve_calibration_auxiliary_authority + ).parameters + + record = _record( + authorization_receipt_released_at=AUTHORIZATION_RECEIPT_RELEASED_AT + ) + assert ( + record.authorization_receipt_released_at + == AUTHORIZATION_RECEIPT_RELEASED_AT + ) + + +def test_authorization_receipt_cannot_retroactively_authorize_interval() -> None: + with pytest.raises(ValueError, match="authorization receipt must be released no later"): + _record( + authorization_receipt_released_at=AUTHORIZED_FROM + timedelta(seconds=1) + ) + + +def test_authorization_receipt_cannot_predate_governing_owner_contract() -> None: + with pytest.raises(ValueError, match="authorization receipt cannot predate owner contract"): + _record( + authorization_receipt_released_at=OWNER_CONTRACT_RELEASED_AT + - timedelta(seconds=1) + ) + + +def test_authorization_receipt_release_requires_timezone_aware_evidence() -> None: + with pytest.raises(ValueError): + _record(authorization_receipt_released_at=datetime(2026, 8, 31)) diff --git a/services/workforce-validation-api/tests/test_calibration_benchmark_authority.py b/services/workforce-validation-api/tests/test_calibration_benchmark_authority.py new file mode 100644 index 000000000..9382c9940 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_benchmark_authority.py @@ -0,0 +1,336 @@ +"""Fail-closed contract for released calibration benchmark authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.benchmark_authority import ( + CalibrationBenchmarkAuthorityIntegrityError, + CalibrationBenchmarkAuthorityNotFound, + CalibrationBenchmarkAuthorityReadPort, + CalibrationBenchmarkAuthorityRecord, + CalibrationBenchmarkAuthorityView, + resolve_calibration_benchmark_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +BENCHMARK_REFERENCE = "calibration_benchmark_receipt:11111111-1111-4111-8111-111111111111" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +BENCHMARK_DIGEST = "1" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +BENCHMARK_REFERENCE_AT = datetime(2026, 6, 30, tzinfo=timezone.utc) +BENCHMARK_RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_receipt_digest", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_reference_at", + "benchmark_receipt_released_at", + "owner_contract_released_at", + } +) + + +class _ReadPort: + """Return configured benchmark authority and retain exact lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[tuple[object, ...]] = [] + + def read_calibration_benchmark_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + ) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append( + ( + tenant_record_id, + validity_study_id, + benchmark_receipt_reference, + benchmark_receipt_version, + benchmark_receipt_digest, + benchmark_owner_contract_reference, + benchmark_owner_contract_version, + benchmark_owner_contract_digest, + benchmark_reference_at, + ) + ) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(CalibrationBenchmarkAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must be rejected without executing it.""" + + @property + def read_calibration_benchmark_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-benchmark-authority-read-v1", + resource_kind="calibration_benchmark_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> CalibrationBenchmarkAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "benchmark_receipt_reference": BENCHMARK_REFERENCE, + "benchmark_receipt_version": 4, + "benchmark_receipt_digest": BENCHMARK_DIGEST, + "benchmark_owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "benchmark_owner_contract_version": 3, + "benchmark_owner_contract_digest": OWNER_CONTRACT_DIGEST, + "benchmark_reference_at": BENCHMARK_REFERENCE_AT, + "benchmark_receipt_released_at": BENCHMARK_RELEASED_AT, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + } + values.update(overrides) + return CalibrationBenchmarkAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> CalibrationBenchmarkAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "benchmark_receipt_reference": BENCHMARK_REFERENCE, + "benchmark_receipt_version": 4, + "benchmark_receipt_digest": BENCHMARK_DIGEST, + "benchmark_owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "benchmark_owner_contract_version": 3, + "benchmark_owner_contract_digest": OWNER_CONTRACT_DIGEST, + "benchmark_reference_at": BENCHMARK_REFERENCE_AT, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_calibration_benchmark_authority(**values) + + +def test_resolution_authorizes_then_returns_released_minimized_evidence() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, CalibrationBenchmarkAuthorityReadPort) + assert port.calls == [ + ( + TENANT, + STUDY, + BENCHMARK_REFERENCE, + 4, + BENCHMARK_DIGEST, + OWNER_CONTRACT_REFERENCE, + 3, + OWNER_CONTRACT_DIGEST, + BENCHMARK_REFERENCE_AT, + ) + ] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert view.fields == ( + ("benchmark_owner_contract_digest", OWNER_CONTRACT_DIGEST), + ("benchmark_owner_contract_reference", OWNER_CONTRACT_REFERENCE), + ("benchmark_owner_contract_version", 3), + ("benchmark_receipt_digest", BENCHMARK_DIGEST), + ("benchmark_receipt_reference", BENCHMARK_REFERENCE), + ("benchmark_receipt_released_at", BENCHMARK_RELEASED_AT), + ("benchmark_receipt_version", 4), + ("benchmark_reference_at", BENCHMARK_REFERENCE_AT), + ("owner_contract_released_at", OWNER_CONTRACT_RELEASED_AT), + ) + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(CalibrationBenchmarkAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + + with pytest.raises(CalibrationBenchmarkAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + { + "benchmark_receipt_reference": ( + "calibration_benchmark_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ) + }, + {"benchmark_receipt_version": 5}, + {"benchmark_receipt_digest": "a" * 64}, + { + "benchmark_owner_contract_reference": ( + "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + ) + }, + {"benchmark_owner_contract_version": 4}, + {"benchmark_owner_contract_digest": "b" * 64}, + {"benchmark_reference_at": BENCHMARK_REFERENCE_AT + timedelta(seconds=1)}, + ], +) +def test_owner_evidence_must_match_every_leaf_benchmark_coordinate( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(CalibrationBenchmarkAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_owner_evidence_must_have_existed_before_scientific_use() -> None: + for overrides in ( + {"benchmark_receipt_released_at": USED_AT + timedelta(seconds=1)}, + { + "benchmark_receipt_released_at": USED_AT + timedelta(seconds=1), + "owner_contract_released_at": USED_AT + timedelta(seconds=1), + }, + {"benchmark_reference_at": USED_AT + timedelta(seconds=1)}, + ): + port = _ReadPort(_record(**overrides)) + with pytest.raises(CalibrationBenchmarkAuthorityIntegrityError): + _resolve( + read_port=port, + benchmark_reference_at=overrides.get( + "benchmark_reference_at", BENCHMARK_REFERENCE_AT + ), + ) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("benchmark_receipt_reference", "wrong:benchmark", ValueError), + ("benchmark_receipt_version", True, ValueError), + ("benchmark_receipt_digest", "ABC", ValueError), + ("benchmark_owner_contract_reference", "wrong:contract", ValueError), + ("benchmark_owner_contract_version", 0, ValueError), + ("benchmark_owner_contract_digest", "2" * 63, ValueError), + ("benchmark_reference_at", datetime(2026, 6, 30), ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value # type: ignore[assignment] + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +@pytest.mark.parametrize( + ("key", "value"), + [ + ("tenant_record_id", UUID(int=0)), + ("validity_study_id", "not-a-uuid"), + ("benchmark_receipt_reference", "wrong:benchmark"), + ("benchmark_receipt_version", 0), + ("benchmark_receipt_digest", "1" * 63), + ("benchmark_owner_contract_reference", "wrong:contract"), + ("benchmark_owner_contract_version", True), + ("benchmark_owner_contract_digest", "2" * 65), + ("benchmark_reference_at", datetime(2026, 6, 30)), + ("benchmark_receipt_released_at", datetime(2026, 7, 15)), + ("owner_contract_released_at", "not-a-datetime"), + ], +) +def test_record_rejects_invalid_released_benchmark_evidence(key: str, value: object) -> None: + with pytest.raises((TypeError, ValueError)): + _record(**{key: value}) + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(record, "benchmark_receipt_version", 999) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + CalibrationBenchmarkAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) diff --git a/services/workforce-validation-api/tests/test_calibration_benchmark_authority_edges.py b/services/workforce-validation-api/tests/test_calibration_benchmark_authority_edges.py new file mode 100644 index 000000000..bed793b8b --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_benchmark_authority_edges.py @@ -0,0 +1,54 @@ +"""Branch-complete scalar edges for calibration benchmark authority.""" + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.benchmark_authority import ( + CalibrationBenchmarkAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +REFERENCE_AT = datetime(2026, 6, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> CalibrationBenchmarkAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "benchmark_receipt_reference": ( + "calibration_benchmark_receipt:11111111-1111-4111-8111-111111111111" + ), + "benchmark_receipt_version": 4, + "benchmark_receipt_digest": "1" * 64, + "benchmark_owner_contract_reference": ( + "released_owner_contract:22222222-2222-4222-8222-222222222222" + ), + "benchmark_owner_contract_version": 3, + "benchmark_owner_contract_digest": "2" * 64, + "benchmark_reference_at": REFERENCE_AT, + "benchmark_receipt_released_at": RELEASED_AT, + "owner_contract_released_at": RELEASED_AT, + } + values.update(overrides) + return CalibrationBenchmarkAuthorityRecord(**values) + + +@pytest.mark.parametrize( + ("field_name", "value"), + [ + ("benchmark_receipt_reference", object()), + ("benchmark_receipt_reference", "not namespaced"), + ("benchmark_owner_contract_reference", object()), + ("benchmark_receipt_digest", object()), + ("benchmark_owner_contract_digest", 7), + ], +) +def test_scalar_type_and_reference_shape_edges_fail_closed( + field_name: str, value: object +) -> None: + with pytest.raises(ValueError): + _record(**{field_name: value}) diff --git a/services/workforce-validation-api/tests/test_calibration_benchmark_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_calibration_benchmark_authority_structural_integrity.py new file mode 100644 index 000000000..a9206b27a --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_benchmark_authority_structural_integrity.py @@ -0,0 +1,123 @@ +"""Regression coverage for canonical calibration-benchmark owner structure.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.benchmark_authority import ( + CalibrationBenchmarkAuthorityIntegrityError, + CalibrationBenchmarkAuthorityRecord, + resolve_calibration_benchmark_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +BENCHMARK_REFERENCE = "calibration_benchmark_receipt:11111111-1111-4111-8111-111111111111" +OWNER_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +BENCHMARK_DIGEST = "1" * 64 +OWNER_DIGEST = "2" * 64 +REFERENCE_AT = datetime(2026, 6, 30, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_receipt_digest", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_reference_at", + "benchmark_receipt_released_at", + "owner_contract_released_at", + } +) + + +class _ReadPort: + """Return configured owner evidence without normalizing tuple structure.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_calibration_benchmark_authority(self, **_: object) -> object: + """Return the configured raw owner result.""" + return self.result + + +def _record() -> CalibrationBenchmarkAuthorityRecord: + """Build one current canonical calibration-benchmark owner record.""" + return CalibrationBenchmarkAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + benchmark_receipt_reference=BENCHMARK_REFERENCE, + benchmark_receipt_version=4, + benchmark_receipt_digest=BENCHMARK_DIGEST, + benchmark_owner_contract_reference=OWNER_REFERENCE, + benchmark_owner_contract_version=3, + benchmark_owner_contract_digest=OWNER_DIGEST, + benchmark_reference_at=REFERENCE_AT, + benchmark_receipt_released_at=RELEASED_AT, + owner_contract_released_at=OWNER_RELEASED_AT, + ) + + +def _resolve(read_port: object) -> object: + """Resolve canonical coordinates through a supplied raw owner port.""" + principal = ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-benchmark-authority-read-v1", + resource_kind="calibration_benchmark_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + return resolve_calibration_benchmark_authority( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + benchmark_receipt_reference=BENCHMARK_REFERENCE, + benchmark_receipt_version=4, + benchmark_receipt_digest=BENCHMARK_DIGEST, + benchmark_owner_contract_reference=OWNER_REFERENCE, + benchmark_owner_contract_version=3, + benchmark_owner_contract_digest=OWNER_DIGEST, + benchmark_reference_at=REFERENCE_AT, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=read_port, + ) + + +def test_owner_port_cannot_append_hidden_tuple_fields_to_exact_benchmark_record() -> None: + """Reject exact-typed evidence with coordinates outside the canonical tuple.""" + valid = _record() + forged = tuple.__new__( + CalibrationBenchmarkAuthorityRecord, + (*tuple(valid), "hidden-unreviewed-owner-coordinate"), + ) + + with pytest.raises(CalibrationBenchmarkAuthorityIntegrityError): + _resolve(_ReadPort(forged)) + + +def test_malformed_exact_benchmark_record_maps_to_integrity_error() -> None: + """Map truncated exact-typed evidence to the domain integrity boundary.""" + valid = _record() + forged = tuple.__new__(CalibrationBenchmarkAuthorityRecord, tuple(valid)[:-1]) + + with pytest.raises(CalibrationBenchmarkAuthorityIntegrityError): + _resolve(_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_calibration_benchmark_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_calibration_benchmark_authority_view_issuance_integrity.py new file mode 100644 index 000000000..1750ecb26 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_benchmark_authority_view_issuance_integrity.py @@ -0,0 +1,73 @@ +"""Regression contract for calibration-benchmark view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.benchmark_authority as authority_module +from orgmetra_workforce_validation_api.benchmark_authority import ( + CalibrationBenchmarkAuthorityIntegrityError, + CalibrationBenchmarkAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_importable_marker_cannot_mint_calibration_benchmark_view() -> None: + """Keep the benchmark view seal outside importable module state.""" + marker_name = "_CALIBRATION_BENCHMARK_VIEW_ISSUANCE_MARKER" + assert not hasattr(authority_module, marker_name) + + forged_view = object.__new__(CalibrationBenchmarkAuthorityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__(forged_view, "_fields", ()) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises(CalibrationBenchmarkAuthorityIntegrityError): + _ = forged_view.fields + + +def test_low_level_tuple_construction_cannot_issue_benchmark_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + CalibrationBenchmarkAuthorityView, + (TENANT.int, STUDY.int, (("benchmark_receipt_digest", "0" * 64),)), + ) + + +def test_unsealed_object_allocation_cannot_expose_benchmark_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(CalibrationBenchmarkAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + CalibrationBenchmarkAuthorityIntegrityError, + match="was not issued by resolve_calibration_benchmark_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_benchmark_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(CalibrationBenchmarkAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + CalibrationBenchmarkAuthorityIntegrityError, + match="was not issued by resolve_calibration_benchmark_authority", + ): + _ = forged_view.fields + + +def test_raw_benchmark_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(CalibrationBenchmarkAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_calibration_benchmark_supersession.py b/services/workforce-validation-api/tests/test_calibration_benchmark_supersession.py new file mode 100644 index 000000000..72592c977 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_benchmark_supersession.py @@ -0,0 +1,249 @@ +"""Fail closed when released calibration benchmark evidence has been superseded.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.benchmark_authority import ( + CalibrationBenchmarkAuthorityIntegrityError, + CalibrationBenchmarkAuthorityRecord, + resolve_calibration_benchmark_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +BENCHMARK_REFERENCE = "calibration_benchmark_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR_REFERENCE = "calibration_benchmark_receipt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +BENCHMARK_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +BENCHMARK_REFERENCE_AT = datetime(2026, 6, 30, tzinfo=timezone.utc) +BENCHMARK_RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "benchmark_receipt_reference", + "benchmark_receipt_version", + "benchmark_receipt_digest", + "benchmark_owner_contract_reference", + "benchmark_owner_contract_version", + "benchmark_owner_contract_digest", + "benchmark_reference_at", + "benchmark_receipt_released_at", + "owner_contract_released_at", + } +) + + +class _ReadPort: + """Return one configured owner record through the canonical benchmark read shape.""" + + def __init__(self, record: CalibrationBenchmarkAuthorityRecord) -> None: + self.record = record + + def read_calibration_benchmark_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + benchmark_receipt_reference: str, + benchmark_receipt_version: int, + benchmark_receipt_digest: str, + benchmark_owner_contract_reference: str, + benchmark_owner_contract_version: int, + benchmark_owner_contract_digest: str, + benchmark_reference_at: datetime, + ) -> CalibrationBenchmarkAuthorityRecord: + """Return the owner-resolved record; resolver verifies every requested coordinate.""" + return self.record + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy() -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-benchmark-authority-read-v1", + resource_kind="calibration_benchmark_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record( + *, + superseded_at: datetime | None, + successor_reference: str | None, + successor_version: int | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> CalibrationBenchmarkAuthorityRecord: + return CalibrationBenchmarkAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + benchmark_receipt_reference=BENCHMARK_REFERENCE, + benchmark_receipt_version=4, + benchmark_receipt_digest=BENCHMARK_DIGEST, + benchmark_owner_contract_reference=OWNER_CONTRACT_REFERENCE, + benchmark_owner_contract_version=3, + benchmark_owner_contract_digest=OWNER_CONTRACT_DIGEST, + benchmark_reference_at=BENCHMARK_REFERENCE_AT, + benchmark_receipt_released_at=BENCHMARK_RELEASED_AT, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + benchmark_receipt_superseded_at=superseded_at, + successor_benchmark_receipt_reference=successor_reference, + successor_benchmark_receipt_version=successor_version, + successor_benchmark_receipt_digest=successor_digest, + successor_benchmark_receipt_released_at=successor_released_at, + ) + + +def _resolve(record: CalibrationBenchmarkAuthorityRecord, *, used_at: datetime = USED_AT): + return resolve_calibration_benchmark_authority( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + benchmark_receipt_reference=BENCHMARK_REFERENCE, + benchmark_receipt_version=4, + benchmark_receipt_digest=BENCHMARK_DIGEST, + benchmark_owner_contract_reference=OWNER_CONTRACT_REFERENCE, + benchmark_owner_contract_version=3, + benchmark_owner_contract_digest=OWNER_CONTRACT_DIGEST, + benchmark_reference_at=BENCHMARK_REFERENCE_AT, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=_ReadPort(record), + ) + + +def test_historical_use_before_supersession_remains_verifiable_without_leaking_lineage() -> None: + superseded_at = USED_AT + timedelta(days=1) + view = _resolve( + _record( + superseded_at=superseded_at, + successor_reference=SUCCESSOR_REFERENCE, + successor_version=5, + successor_digest=SUCCESSOR_DIGEST, + successor_released_at=superseded_at, + ) + ) + + fields = dict(view.fields) + assert fields["benchmark_receipt_reference"] == BENCHMARK_REFERENCE + assert "benchmark_receipt_superseded_at" not in fields + assert "successor_benchmark_receipt_reference" not in fields + assert "successor_benchmark_receipt_released_at" not in fields + + +def test_successor_benchmark_release_must_equal_predecessor_cutover() -> None: + superseded_at = USED_AT + timedelta(days=1) + with pytest.raises( + ValueError, + match="successor benchmark receipt must be released exactly at supersession", + ): + _record( + superseded_at=superseded_at, + successor_reference=SUCCESSOR_REFERENCE, + successor_version=5, + successor_digest=SUCCESSOR_DIGEST, + successor_released_at=superseded_at - timedelta(seconds=1), + ) + + +def test_benchmark_receipt_cannot_predate_its_released_owner_contract() -> None: + with pytest.raises(ValueError, match="owner contract must be released no later than benchmark receipt"): + CalibrationBenchmarkAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + benchmark_receipt_reference=BENCHMARK_REFERENCE, + benchmark_receipt_version=4, + benchmark_receipt_digest=BENCHMARK_DIGEST, + benchmark_owner_contract_reference=OWNER_CONTRACT_REFERENCE, + benchmark_owner_contract_version=3, + benchmark_owner_contract_digest=OWNER_CONTRACT_DIGEST, + benchmark_reference_at=BENCHMARK_REFERENCE_AT, + benchmark_receipt_released_at=BENCHMARK_RELEASED_AT, + owner_contract_released_at=BENCHMARK_RELEASED_AT + timedelta(seconds=1), + ) + + +def test_benchmark_superseded_by_scientific_use_is_not_authoritative() -> None: + record = _record( + superseded_at=USED_AT, + successor_reference=SUCCESSOR_REFERENCE, + successor_version=5, + successor_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT, + ) + + with pytest.raises(CalibrationBenchmarkAuthorityIntegrityError): + _resolve(record) + + +@pytest.mark.parametrize( + ( + "superseded_at", + "successor_reference", + "successor_version", + "successor_digest", + "successor_released_at", + ), + [ + (USED_AT, None, 5, SUCCESSOR_DIGEST, USED_AT), + (None, SUCCESSOR_REFERENCE, 5, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_REFERENCE, 4, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_REFERENCE, 5, BENCHMARK_DIGEST, USED_AT), + ( + BENCHMARK_RELEASED_AT - timedelta(seconds=1), + SUCCESSOR_REFERENCE, + 5, + SUCCESSOR_DIGEST, + BENCHMARK_RELEASED_AT - timedelta(seconds=1), + ), + ( + USED_AT, + SUCCESSOR_REFERENCE, + 5, + SUCCESSOR_DIGEST, + USED_AT + timedelta(seconds=1), + ), + ( + USED_AT, + SUCCESSOR_REFERENCE, + 5, + SUCCESSOR_DIGEST, + BENCHMARK_RELEASED_AT, + ), + ], +) +def test_owner_record_rejects_incomplete_or_non_append_only_supersession_lineage( + superseded_at: datetime | None, + successor_reference: str | None, + successor_version: int | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> None: + with pytest.raises(ValueError): + _record( + superseded_at=superseded_at, + successor_reference=successor_reference, + successor_version=successor_version, + successor_digest=successor_digest, + successor_released_at=successor_released_at, + ) diff --git a/services/workforce-validation-api/tests/test_calibration_support_authority.py b/services/workforce-validation-api/tests/test_calibration_support_authority.py new file mode 100644 index 000000000..fd13b5c16 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_support_authority.py @@ -0,0 +1,161 @@ +"""Regression contract for released calibration supporting-authority chronology.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.calibration_support_authority import ( + CalibrationSupportAuthorityReadPort, + CalibrationSupportAuthorityRecord, + resolve_calibration_support_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +AUX_OWNER_RELEASED_AT = datetime(2026, 9, 17, 7, 50, tzinfo=timezone.utc) +AUX_AUTH_RELEASED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +AUX_AUTHORIZED_FROM = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +AUX_USE_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +BENCHMARK_OWNER_RELEASED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +BENCHMARK_RECEIPT_RELEASED_AT = datetime(2026, 9, 17, 8, 20, tzinfo=timezone.utc) +BENCHMARK_REFERENCE_AT = datetime(2026, 9, 17, 8, 15, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 9, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 9, 20, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> CalibrationSupportAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "support_authority_reference": ( + "calibration_support_authority:10101010-1010-4010-8010-101010101010" + ), + "support_authority_digest": "0" * 64, + "evidence_version": 1, + "calibration_receipt_reference": ( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + "calibration_receipt_digest": "1" * 64, + "auxiliary_authority_reference": ( + "scientific_auxiliary_authority:22222222-2222-4222-8222-222222222222" + ), + "auxiliary_projection_reference": ( + "calibration_auxiliary_projection:33333333-3333-4333-8333-333333333333" + ), + "auxiliary_projection_version": 3, + "auxiliary_projection_digest": "2" * 64, + "auxiliary_purpose_reference": ( + "scientific_data_use_purpose:44444444-4444-4444-8444-444444444444" + ), + "auxiliary_purpose_digest": "3" * 64, + "auxiliary_owner_contract_reference": ( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + "auxiliary_owner_contract_version": 5, + "auxiliary_owner_contract_digest": "4" * 64, + "auxiliary_owner_contract_released_at": AUX_OWNER_RELEASED_AT, + "auxiliary_authorization_receipt_reference": ( + "scientific_data_authorization:66666666-6666-4666-8666-666666666666" + ), + "auxiliary_authorization_receipt_digest": "5" * 64, + "auxiliary_authorization_receipt_released_at": AUX_AUTH_RELEASED_AT, + "auxiliary_scientific_use_receipt_reference": ( + "scientific_use_receipt:77777777-7777-4777-8777-777777777777" + ), + "auxiliary_scientific_use_receipt_digest": "6" * 64, + "auxiliary_scientific_use_at": AUX_USE_AT, + "auxiliary_authorized_from": AUX_AUTHORIZED_FROM, + "auxiliary_authorized_to": datetime(2026, 10, 1, tzinfo=timezone.utc), + "benchmark_receipt_reference": ( + "calibration_benchmark_receipt:88888888-8888-4888-8888-888888888888" + ), + "benchmark_receipt_version": 8, + "benchmark_receipt_digest": "7" * 64, + "benchmark_owner_contract_reference": ( + "released_owner_contract:99999999-9999-4999-8999-999999999999" + ), + "benchmark_owner_contract_version": 9, + "benchmark_owner_contract_digest": "8" * 64, + "benchmark_owner_contract_released_at": BENCHMARK_OWNER_RELEASED_AT, + "benchmark_reference_at": BENCHMARK_REFERENCE_AT, + "benchmark_receipt_released_at": BENCHMARK_RECEIPT_RELEASED_AT, + "benchmark_receipt_superseded_at": None, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": ( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + "owner_contract_version": 10, + "owner_contract_digest": "9" * 64, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return CalibrationSupportAuthorityRecord(**values) # type: ignore[arg-type] + + +def test_support_chronology_is_owner_evidence_not_lookup_authority() -> None: + record = _record() + read_parameters = signature( + CalibrationSupportAuthorityReadPort.read_calibration_support_authority + ).parameters + resolver_parameters = signature(resolve_calibration_support_authority).parameters + + owner_resolved = { + "auxiliary_owner_contract_released_at", + "auxiliary_authorization_receipt_released_at", + "auxiliary_authorized_from", + "auxiliary_authorized_to", + "benchmark_owner_contract_released_at", + "benchmark_receipt_released_at", + "benchmark_receipt_superseded_at", + "owner_contract_released_at", + "released_at", + } + for field_name in owner_resolved: + assert hasattr(record, field_name) + assert field_name not in read_parameters + assert field_name not in resolver_parameters + + +def test_auxiliary_evidence_must_exist_before_the_committed_scientific_use() -> None: + with pytest.raises(ValueError, match="authorization receipt must be released no later"): + _record(auxiliary_authorization_receipt_released_at=AUX_USE_AT + timedelta(seconds=1)) + + with pytest.raises(ValueError, match="auxiliary owner contract cannot postdate authorization"): + _record(auxiliary_owner_contract_released_at=AUX_AUTH_RELEASED_AT + timedelta(seconds=1)) + + +def test_auxiliary_use_must_be_inside_owner_resolved_authorization_interval() -> None: + with pytest.raises(ValueError, match="auxiliary scientific use must fall inside"): + _record(auxiliary_authorized_from=AUX_USE_AT + timedelta(seconds=1)) + + with pytest.raises(ValueError, match="auxiliary scientific use must fall inside"): + _record(auxiliary_authorized_to=AUX_USE_AT) + + +def test_supporting_evidence_must_exist_before_calibration_construction() -> None: + with pytest.raises(ValueError, match="benchmark receipt must be released no later"): + _record(benchmark_receipt_released_at=CONSTRUCTED_AT + timedelta(seconds=1)) + + with pytest.raises(ValueError, match="auxiliary scientific use cannot be later"): + _record(auxiliary_scientific_use_at=CONSTRUCTED_AT + timedelta(seconds=1)) + + +def test_superseded_benchmark_cannot_support_later_calibration_construction() -> None: + with pytest.raises(ValueError, match="superseded benchmark cannot support calibration"): + _record(benchmark_receipt_superseded_at=CONSTRUCTED_AT) + + +def test_support_chronology_requires_timezone_aware_owner_evidence() -> None: + with pytest.raises(ValueError): + _record(benchmark_receipt_released_at=datetime(2026, 9, 17, 8, 20)) + + +def test_application_owner_contract_cannot_retroactively_authorize_support_binding() -> None: + with pytest.raises(ValueError, match="owner contract cannot be released after support evidence"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(seconds=1)) diff --git a/services/workforce-validation-api/tests/test_calibration_support_authority_edges.py b/services/workforce-validation-api/tests/test_calibration_support_authority_edges.py new file mode 100644 index 000000000..533e63890 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_support_authority_edges.py @@ -0,0 +1,409 @@ +"""Fail-closed and branch coverage for calibration supporting-authority evidence.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +import orgmetra_workforce_validation_api.calibration_support_authority as target +from orgmetra_workforce_validation_api.calibration_support_authority import ( + CalibrationSupportAuthorityIntegrityError, + CalibrationSupportAuthorityNotFound, + CalibrationSupportAuthorityReadPort, + CalibrationSupportAuthorityRecord, + CalibrationSupportAuthorityView, + resolve_calibration_support_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000f3") +AUX_OWNER_RELEASED_AT = datetime(2026, 9, 17, 7, 50, tzinfo=timezone.utc) +AUX_AUTH_RELEASED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +AUX_AUTHORIZED_FROM = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +AUX_AUTHORIZED_TO = datetime(2026, 10, 1, tzinfo=timezone.utc) +AUX_USE_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +BENCHMARK_OWNER_RELEASED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +BENCHMARK_RECEIPT_RELEASED_AT = datetime(2026, 9, 17, 8, 20, tzinfo=timezone.utc) +BENCHMARK_REFERENCE_AT = datetime(2026, 9, 17, 8, 15, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 9, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 9, 20, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 10, 0, tzinfo=timezone.utc) + + +class _ReadPort: + """Return configured support evidence and retain whether persistence was invoked.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls = 0 + + def read_calibration_support_authority(self, **_: object) -> object: + """Return configured evidence after counting the owner read.""" + self.calls += 1 + return self.result + + +class _NoReadMethod: + """Deliberately omit the owner-read capability.""" + + +class _ProtocolOnly(CalibrationSupportAuthorityReadPort): + """Inherit only the Protocol placeholder.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must not execute during static capability inspection.""" + + @property + def read_calibration_support_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-support-read-v1", + resource_kind="calibration_support_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=target._READ_FIELDS, + ) + + +def _record(**overrides: object) -> CalibrationSupportAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "support_authority_reference": ( + "calibration_support_authority:10101010-1010-4010-8010-101010101010" + ), + "support_authority_digest": "0" * 64, + "evidence_version": 1, + "calibration_receipt_reference": ( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + "calibration_receipt_digest": "1" * 64, + "auxiliary_authority_reference": ( + "scientific_auxiliary_authority:22222222-2222-4222-8222-222222222222" + ), + "auxiliary_projection_reference": ( + "calibration_auxiliary_projection:33333333-3333-4333-8333-333333333333" + ), + "auxiliary_projection_version": 3, + "auxiliary_projection_digest": "2" * 64, + "auxiliary_purpose_reference": ( + "scientific_data_use_purpose:44444444-4444-4444-8444-444444444444" + ), + "auxiliary_purpose_digest": "3" * 64, + "auxiliary_owner_contract_reference": ( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + "auxiliary_owner_contract_version": 5, + "auxiliary_owner_contract_digest": "4" * 64, + "auxiliary_owner_contract_released_at": AUX_OWNER_RELEASED_AT, + "auxiliary_authorization_receipt_reference": ( + "scientific_data_authorization:66666666-6666-4666-8666-666666666666" + ), + "auxiliary_authorization_receipt_digest": "5" * 64, + "auxiliary_authorization_receipt_released_at": AUX_AUTH_RELEASED_AT, + "auxiliary_scientific_use_receipt_reference": ( + "scientific_use_receipt:77777777-7777-4777-8777-777777777777" + ), + "auxiliary_scientific_use_receipt_digest": "6" * 64, + "auxiliary_scientific_use_at": AUX_USE_AT, + "auxiliary_authorized_from": AUX_AUTHORIZED_FROM, + "auxiliary_authorized_to": AUX_AUTHORIZED_TO, + "benchmark_receipt_reference": ( + "calibration_benchmark_receipt:88888888-8888-4888-8888-888888888888" + ), + "benchmark_receipt_version": 8, + "benchmark_receipt_digest": "7" * 64, + "benchmark_owner_contract_reference": ( + "released_owner_contract:99999999-9999-4999-8999-999999999999" + ), + "benchmark_owner_contract_version": 9, + "benchmark_owner_contract_digest": "8" * 64, + "benchmark_owner_contract_released_at": BENCHMARK_OWNER_RELEASED_AT, + "benchmark_reference_at": BENCHMARK_REFERENCE_AT, + "benchmark_receipt_released_at": BENCHMARK_RECEIPT_RELEASED_AT, + "benchmark_receipt_superseded_at": None, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": ( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + "owner_contract_version": 10, + "owner_contract_digest": "9" * 64, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return CalibrationSupportAuthorityRecord(**values) # type: ignore[arg-type] + + +def _resolve(*, read_port: object, **overrides: object) -> CalibrationSupportAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "calibration_receipt_reference": ( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + "calibration_receipt_digest": "1" * 64, + "auxiliary_authority_reference": ( + "scientific_auxiliary_authority:22222222-2222-4222-8222-222222222222" + ), + "auxiliary_projection_reference": ( + "calibration_auxiliary_projection:33333333-3333-4333-8333-333333333333" + ), + "auxiliary_projection_version": 3, + "auxiliary_projection_digest": "2" * 64, + "auxiliary_purpose_reference": ( + "scientific_data_use_purpose:44444444-4444-4444-8444-444444444444" + ), + "auxiliary_purpose_digest": "3" * 64, + "auxiliary_owner_contract_reference": ( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + "auxiliary_owner_contract_version": 5, + "auxiliary_owner_contract_digest": "4" * 64, + "auxiliary_authorization_receipt_reference": ( + "scientific_data_authorization:66666666-6666-4666-8666-666666666666" + ), + "auxiliary_authorization_receipt_digest": "5" * 64, + "auxiliary_scientific_use_receipt_reference": ( + "scientific_use_receipt:77777777-7777-4777-8777-777777777777" + ), + "auxiliary_scientific_use_receipt_digest": "6" * 64, + "auxiliary_scientific_use_at": AUX_USE_AT, + "benchmark_receipt_reference": ( + "calibration_benchmark_receipt:88888888-8888-4888-8888-888888888888" + ), + "benchmark_receipt_version": 8, + "benchmark_receipt_digest": "7" * 64, + "benchmark_owner_contract_reference": ( + "released_owner_contract:99999999-9999-4999-8999-999999999999" + ), + "benchmark_owner_contract_version": 9, + "benchmark_owner_contract_digest": "8" * 64, + "benchmark_reference_at": BENCHMARK_REFERENCE_AT, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": ( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + "owner_contract_version": 10, + "owner_contract_digest": "9" * 64, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_calibration_support_authority(**values) # type: ignore[arg-type] + + +def test_resolution_returns_complete_owner_resolved_support_chronology() -> None: + port = _ReadPort(_record()) + view = _resolve(read_port=port) + + assert isinstance(port, CalibrationSupportAuthorityReadPort) + assert port.calls == 1 + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["support_authority_digest"] == "0" * 64 + assert fields["auxiliary_owner_contract_released_at"] == AUX_OWNER_RELEASED_AT + assert fields["auxiliary_authorization_receipt_released_at"] == AUX_AUTH_RELEASED_AT + assert fields["benchmark_receipt_released_at"] == BENCHMARK_RECEIPT_RELEASED_AT + assert fields["benchmark_receipt_superseded_at"] is None + assert fields["released_at"] == RELEASED_AT + + +def test_open_ended_auxiliary_interval_and_future_benchmark_cutover_remain_reproducible() -> None: + future_cutover = CONSTRUCTED_AT + timedelta(hours=1) + view = _resolve( + read_port=_ReadPort( + _record( + auxiliary_authorized_to=None, + benchmark_receipt_superseded_at=future_cutover, + ) + ) + ) + fields = dict(view.fields) + assert fields["auxiliary_authorized_to"] is None + assert fields["benchmark_receipt_superseded_at"] == future_cutover + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == 0 + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(CalibrationSupportAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(CalibrationSupportAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +def test_owner_result_must_match_requested_coordinates() -> None: + with pytest.raises(CalibrationSupportAuthorityIntegrityError, match="does not match"): + _resolve(read_port=_ReadPort(_record(tenant_record_id=OTHER_TENANT))) + with pytest.raises(CalibrationSupportAuthorityIntegrityError, match="does not match"): + _resolve(read_port=_ReadPort(_record(validity_study_id=OTHER_STUDY))) + with pytest.raises(CalibrationSupportAuthorityIntegrityError, match="does not match"): + _resolve(read_port=_ReadPort(_record(calibration_receipt_digest="a" * 64))) + + +def test_support_binding_must_be_released_before_scientific_use() -> None: + with pytest.raises(CalibrationSupportAuthorityIntegrityError, match="released before scientific use"): + _resolve(read_port=_ReadPort(_record()), used_at=RELEASED_AT - timedelta(seconds=1)) + + +@pytest.mark.parametrize( + ("override", "match"), + [ + ({"evidence_version": 2}, "evidence_version must remain 1"), + ( + {"auxiliary_owner_contract_released_at": AUX_AUTH_RELEASED_AT + timedelta(seconds=1)}, + "auxiliary owner contract cannot postdate authorization", + ), + ( + {"auxiliary_authorization_receipt_released_at": AUX_USE_AT + timedelta(seconds=1)}, + "authorization receipt must be released no later", + ), + ( + {"auxiliary_authorized_to": AUX_AUTHORIZED_FROM}, + "auxiliary_authorized_to must be later", + ), + ( + {"auxiliary_authorized_from": AUX_USE_AT + timedelta(seconds=1)}, + "auxiliary scientific use must fall inside", + ), + ( + {"auxiliary_authorized_to": AUX_USE_AT}, + "auxiliary scientific use must fall inside", + ), + ( + {"benchmark_owner_contract_released_at": BENCHMARK_RECEIPT_RELEASED_AT + timedelta(seconds=1)}, + "benchmark owner contract cannot postdate", + ), + ( + {"benchmark_receipt_superseded_at": BENCHMARK_RECEIPT_RELEASED_AT}, + "benchmark supersession must be later", + ), + ( + {"auxiliary_scientific_use_at": CONSTRUCTED_AT + timedelta(seconds=1)}, + "auxiliary scientific use cannot be later", + ), + ( + {"benchmark_reference_at": CONSTRUCTED_AT + timedelta(seconds=1)}, + "benchmark reference cannot be later", + ), + ( + {"benchmark_receipt_released_at": CONSTRUCTED_AT + timedelta(seconds=1)}, + "benchmark receipt must be released no later", + ), + ( + {"benchmark_receipt_superseded_at": CONSTRUCTED_AT}, + "superseded benchmark cannot support calibration", + ), + ( + {"owner_contract_released_at": RELEASED_AT + timedelta(seconds=1)}, + "owner contract cannot be released after support evidence", + ), + ( + { + "owner_contract_released_at": CONSTRUCTED_AT - timedelta(seconds=2), + "released_at": CONSTRUCTED_AT - timedelta(seconds=1), + }, + "support evidence cannot be released before calibration construction", + ), + ], +) +def test_record_rejects_incoherent_owner_chronology( + override: dict[str, object], match: str +) -> None: + with pytest.raises(ValueError, match=match): + _record(**override) + + +def test_view_cannot_be_constructed_by_callers() -> None: + with pytest.raises(TypeError): + CalibrationSupportAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("calibration_receipt_reference", "wrong:calibration", ValueError), + ("calibration_receipt_digest", "ABC", ValueError), + ("auxiliary_authority_reference", "wrong:aux", ValueError), + ("auxiliary_projection_reference", "wrong:projection", ValueError), + ("auxiliary_projection_version", 0, ValueError), + ("auxiliary_projection_digest", "2" * 63, ValueError), + ("auxiliary_purpose_reference", "wrong:purpose", ValueError), + ("auxiliary_purpose_digest", "3" * 63, ValueError), + ("auxiliary_owner_contract_reference", "wrong:contract", ValueError), + ("auxiliary_owner_contract_version", True, ValueError), + ("auxiliary_owner_contract_digest", "4" * 63, ValueError), + ("auxiliary_authorization_receipt_reference", "wrong:authorization", ValueError), + ("auxiliary_authorization_receipt_digest", "5" * 63, ValueError), + ("auxiliary_scientific_use_receipt_reference", "wrong:use", ValueError), + ("auxiliary_scientific_use_receipt_digest", "6" * 63, ValueError), + ("auxiliary_scientific_use_at", datetime(2026, 9, 17, 8, 30), ValueError), + ("benchmark_receipt_reference", "wrong:benchmark", ValueError), + ("benchmark_receipt_version", 0, ValueError), + ("benchmark_receipt_digest", "7" * 63, ValueError), + ("benchmark_owner_contract_reference", "wrong:contract", ValueError), + ("benchmark_owner_contract_version", 0, ValueError), + ("benchmark_owner_contract_digest", "8" * 63, ValueError), + ("benchmark_reference_at", datetime(2026, 9, 17, 8, 15), ValueError), + ("constructed_at", datetime(2026, 9, 17, 9, 0), ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "9" * 63, ValueError), + ("used_at", datetime(2026, 9, 17, 10, 0), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == 0 diff --git a/services/workforce-validation-api/tests/test_calibration_support_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_calibration_support_authority_structural_integrity.py new file mode 100644 index 000000000..c5acd9d7d --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_support_authority_structural_integrity.py @@ -0,0 +1,201 @@ +"""Structural-integrity regressions for calibration support owner evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +import orgmetra_workforce_validation_api.calibration_support_authority as target +from orgmetra_workforce_validation_api.calibration_support_authority import ( + CalibrationSupportAuthorityIntegrityError, + CalibrationSupportAuthorityRecord, + resolve_calibration_support_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +AUX_OWNER_RELEASED_AT = datetime(2026, 9, 17, 7, 50, tzinfo=timezone.utc) +AUX_AUTH_RELEASED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +AUX_AUTHORIZED_FROM = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +AUX_AUTHORIZED_TO = datetime(2026, 10, 1, tzinfo=timezone.utc) +AUX_USE_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +BENCHMARK_OWNER_RELEASED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +BENCHMARK_RECEIPT_RELEASED_AT = datetime(2026, 9, 17, 8, 20, tzinfo=timezone.utc) +BENCHMARK_REFERENCE_AT = datetime(2026, 9, 17, 8, 15, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 9, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 9, 20, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 10, 0, tzinfo=timezone.utc) + + +class _ReadPort: + """Return configured persisted evidence through the owner-read capability.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_calibration_support_authority(self, **_: object) -> object: + """Return the configured owner evidence.""" + return self.result + + +def _record() -> CalibrationSupportAuthorityRecord: + return CalibrationSupportAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + support_authority_reference=( + "calibration_support_authority:10101010-1010-4010-8010-101010101010" + ), + support_authority_digest="0" * 64, + evidence_version=1, + calibration_receipt_reference=( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + calibration_receipt_digest="1" * 64, + auxiliary_authority_reference=( + "scientific_auxiliary_authority:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:33333333-3333-4333-8333-333333333333" + ), + auxiliary_projection_version=3, + auxiliary_projection_digest="2" * 64, + auxiliary_purpose_reference=( + "scientific_data_use_purpose:44444444-4444-4444-8444-444444444444" + ), + auxiliary_purpose_digest="3" * 64, + auxiliary_owner_contract_reference=( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + auxiliary_owner_contract_version=5, + auxiliary_owner_contract_digest="4" * 64, + auxiliary_owner_contract_released_at=AUX_OWNER_RELEASED_AT, + auxiliary_authorization_receipt_reference=( + "scientific_data_authorization:66666666-6666-4666-8666-666666666666" + ), + auxiliary_authorization_receipt_digest="5" * 64, + auxiliary_authorization_receipt_released_at=AUX_AUTH_RELEASED_AT, + auxiliary_scientific_use_receipt_reference=( + "scientific_use_receipt:77777777-7777-4777-8777-777777777777" + ), + auxiliary_scientific_use_receipt_digest="6" * 64, + auxiliary_scientific_use_at=AUX_USE_AT, + auxiliary_authorized_from=AUX_AUTHORIZED_FROM, + auxiliary_authorized_to=AUX_AUTHORIZED_TO, + benchmark_receipt_reference=( + "calibration_benchmark_receipt:88888888-8888-4888-8888-888888888888" + ), + benchmark_receipt_version=8, + benchmark_receipt_digest="7" * 64, + benchmark_owner_contract_reference=( + "released_owner_contract:99999999-9999-4999-8999-999999999999" + ), + benchmark_owner_contract_version=9, + benchmark_owner_contract_digest="8" * 64, + benchmark_owner_contract_released_at=BENCHMARK_OWNER_RELEASED_AT, + benchmark_reference_at=BENCHMARK_REFERENCE_AT, + benchmark_receipt_released_at=BENCHMARK_RECEIPT_RELEASED_AT, + benchmark_receipt_superseded_at=None, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + owner_contract_version=10, + owner_contract_digest="9" * 64, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def _resolve(result: object) -> object: + return resolve_calibration_support_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + calibration_receipt_reference=( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + calibration_receipt_digest="1" * 64, + auxiliary_authority_reference=( + "scientific_auxiliary_authority:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:33333333-3333-4333-8333-333333333333" + ), + auxiliary_projection_version=3, + auxiliary_projection_digest="2" * 64, + auxiliary_purpose_reference=( + "scientific_data_use_purpose:44444444-4444-4444-8444-444444444444" + ), + auxiliary_purpose_digest="3" * 64, + auxiliary_owner_contract_reference=( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + auxiliary_owner_contract_version=5, + auxiliary_owner_contract_digest="4" * 64, + auxiliary_authorization_receipt_reference=( + "scientific_data_authorization:66666666-6666-4666-8666-666666666666" + ), + auxiliary_authorization_receipt_digest="5" * 64, + auxiliary_scientific_use_receipt_reference=( + "scientific_use_receipt:77777777-7777-4777-8777-777777777777" + ), + auxiliary_scientific_use_receipt_digest="6" * 64, + auxiliary_scientific_use_at=AUX_USE_AT, + benchmark_receipt_reference=( + "calibration_benchmark_receipt:88888888-8888-4888-8888-888888888888" + ), + benchmark_receipt_version=8, + benchmark_receipt_digest="7" * 64, + benchmark_owner_contract_reference=( + "released_owner_contract:99999999-9999-4999-8999-999999999999" + ), + benchmark_owner_contract_version=9, + benchmark_owner_contract_digest="8" * 64, + benchmark_reference_at=BENCHMARK_REFERENCE_AT, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + owner_contract_version=10, + owner_contract_digest="9" * 64, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="calibration-support-read-v1", + resource_kind="calibration_support_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=target._READ_FIELDS, + ), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + CalibrationSupportAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(CalibrationSupportAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__(CalibrationSupportAuthorityRecord, tuple(canonical)[:-1]) + + with pytest.raises(CalibrationSupportAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_calibration_support_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_calibration_support_authority_view_issuance_integrity.py new file mode 100644 index 000000000..06ee7ea4d --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_support_authority_view_issuance_integrity.py @@ -0,0 +1,77 @@ +"""Regression contract for calibration-support view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.calibration_support_authority as authority_module +from orgmetra_workforce_validation_api.calibration_support_authority import ( + CalibrationSupportAuthorityIntegrityError, + CalibrationSupportAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_importable_marker_cannot_mint_calibration_support_view() -> None: + """Keep the calibration-support view seal outside importable module state.""" + marker_name = "_CALIBRATION_SUPPORT_VIEW_ISSUANCE_MARKER" + assert not hasattr(authority_module, marker_name) + + forged_view = object.__new__(CalibrationSupportAuthorityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__(forged_view, "_fields", ()) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises(CalibrationSupportAuthorityIntegrityError): + _ = forged_view.fields + + +def test_low_level_tuple_construction_cannot_issue_support_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + CalibrationSupportAuthorityView, + ( + TENANT.int, + STUDY.int, + (("support_authority_digest", "0" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_support_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(CalibrationSupportAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + CalibrationSupportAuthorityIntegrityError, + match="was not issued by resolve_calibration_support_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_support_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(CalibrationSupportAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + CalibrationSupportAuthorityIntegrityError, + match="was not issued by resolve_calibration_support_authority", + ): + _ = forged_view.fields + + +def test_raw_support_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(CalibrationSupportAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_calibration_support_authorization_effective_chronology.py b/services/workforce-validation-api/tests/test_calibration_support_authorization_effective_chronology.py new file mode 100644 index 000000000..7f11185d3 --- /dev/null +++ b/services/workforce-validation-api/tests/test_calibration_support_authorization_effective_chronology.py @@ -0,0 +1,94 @@ +"""RED contract for calibration support authorization release chronology.""" + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.calibration_support_authority import ( + CalibrationSupportAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +AUTHORIZED_FROM = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +USE_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc) + + +def test_authorization_receipt_cannot_be_released_after_interval_begins() -> None: + """Do not accept retroactive authority merely because the receipt predates use.""" + with pytest.raises(ValueError, match="authorization receipt must exist before authorization begins"): + CalibrationSupportAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + support_authority_reference=( + "calibration_support_authority:10101010-1010-4010-8010-101010101010" + ), + support_authority_digest="0" * 64, + evidence_version=1, + calibration_receipt_reference=( + "calibration_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + calibration_receipt_digest="1" * 64, + auxiliary_authority_reference=( + "scientific_auxiliary_authority:22222222-2222-4222-8222-222222222222" + ), + auxiliary_projection_reference=( + "calibration_auxiliary_projection:33333333-3333-4333-8333-333333333333" + ), + auxiliary_projection_version=3, + auxiliary_projection_digest="2" * 64, + auxiliary_purpose_reference=( + "scientific_data_use_purpose:44444444-4444-4444-8444-444444444444" + ), + auxiliary_purpose_digest="3" * 64, + auxiliary_owner_contract_reference=( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + auxiliary_owner_contract_version=5, + auxiliary_owner_contract_digest="4" * 64, + auxiliary_owner_contract_released_at=datetime( + 2026, 9, 17, 7, 50, tzinfo=timezone.utc + ), + auxiliary_authorization_receipt_reference=( + "scientific_data_authorization:66666666-6666-4666-8666-666666666666" + ), + auxiliary_authorization_receipt_digest="5" * 64, + auxiliary_authorization_receipt_released_at=( + AUTHORIZED_FROM + timedelta(seconds=1) + ), + auxiliary_scientific_use_receipt_reference=( + "scientific_use_receipt:77777777-7777-4777-8777-777777777777" + ), + auxiliary_scientific_use_receipt_digest="6" * 64, + auxiliary_scientific_use_at=USE_AT, + auxiliary_authorized_from=AUTHORIZED_FROM, + auxiliary_authorized_to=datetime(2026, 10, 1, tzinfo=timezone.utc), + benchmark_receipt_reference=( + "calibration_benchmark_receipt:88888888-8888-4888-8888-888888888888" + ), + benchmark_receipt_version=8, + benchmark_receipt_digest="7" * 64, + benchmark_owner_contract_reference=( + "released_owner_contract:99999999-9999-4999-8999-999999999999" + ), + benchmark_owner_contract_version=9, + benchmark_owner_contract_digest="8" * 64, + benchmark_owner_contract_released_at=datetime( + 2026, 9, 17, 8, 0, tzinfo=timezone.utc + ), + benchmark_reference_at=datetime(2026, 9, 17, 8, 15, tzinfo=timezone.utc), + benchmark_receipt_released_at=datetime( + 2026, 9, 17, 8, 20, tzinfo=timezone.utc + ), + benchmark_receipt_superseded_at=None, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=( + "released_owner_contract:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + owner_contract_version=10, + owner_contract_digest="9" * 64, + owner_contract_released_at=datetime(2026, 9, 17, 9, 10, tzinfo=timezone.utc), + released_at=datetime(2026, 9, 17, 9, 20, tzinfo=timezone.utc), + ) diff --git a/services/workforce-validation-api/tests/test_final_analysis_weight_authority.py b/services/workforce-validation-api/tests/test_final_analysis_weight_authority.py new file mode 100644 index 000000000..b6b40c6fa --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_analysis_weight_authority.py @@ -0,0 +1,432 @@ +"""Fail-closed contract for complete released final analysis-weight provenance.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityIntegrityError, + FinalAnalysisWeightAuthorityNotFound, + FinalAnalysisWeightAuthorityReadPort, + FinalAnalysisWeightAuthorityRecord, + FinalAnalysisWeightAuthorityView, + FinalWeightAdjustmentCoordinate, + resolve_final_analysis_weight_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +WEIGHT_RECEIPT_REFERENCE = "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" +ESTIMAND_REFERENCE = "validation_estimand:criterion-validity-q3" +TARGET_REFERENCE = "analysis_target_population:workers-2026q3" +WINDOW_REFERENCE = "analysis_window:2026q3" +DURATION_REFERENCE = "analysis_reference_duration:2026q3" +SOURCE_REFERENCE = "source_universe_receipt:22222222-2222-4222-8222-222222222222" +SAMPLING_REFERENCE = "sampling_design_receipt:33333333-3333-4333-8333-333333333333" +ELIGIBILITY_REFERENCE = "weight_eligibility_receipt:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +METHOD_REFERENCE = "weight_method:nonresponse-cell-adjustment" +WEIGHT_RECEIPT_DIGEST = "1" * 64 +ESTIMAND_DIGEST = "2" * 64 +TARGET_DIGEST = "3" * 64 +DURATION_DIGEST = "4" * 64 +ELIGIBLE_CASE_DIGEST = "5" * 64 +ANALYTIC_CASE_DIGEST = "6" * 64 +SOURCE_DIGEST = "7" * 64 +SAMPLING_DIGEST = "8" * 64 +BASE_EVIDENCE_DIGEST = "9" * 64 +BASE_ARTIFACT_DIGEST = "a" * 64 +ADJUSTED_ARTIFACT_DIGEST = "b" * 64 +ADJUSTMENT_CONFIG_DIGEST = "c" * 64 +ADJUSTMENT_RECEIPT_DIGEST = "d" * 64 +ELIGIBILITY_DIGEST = "e" * 64 +OWNER_DIGEST = "f" * 64 +SUPERSEDES_DIGEST = "0" * 64 +CONSTRUCTED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "estimand_reference", + "estimand_digest", + "estimand_scope_code", + "target_population_reference", + "target_population_digest", + "analysis_unit_code", + "analysis_window_reference", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "analytic_case_occurrence_set_digest", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_evidence_digest", + "base_weight_artifact_digest", + "adjustments", + "final_weight_artifact_digest", + "weight_eligibility_receipt_reference", + "weight_eligibility_receipt_digest", + "analytic_case_count", + "constructed_at", + "correction_sequence", + "supersedes_receipt_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured released final-weight authority and capture lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_final_analysis_weight_authority(self, **coordinates: object) -> object: + self.calls.append(dict(coordinates)) + return self.result + + +class _ProtocolOnly(FinalAnalysisWeightAuthorityReadPort): + """Inherit only the Protocol placeholder.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must not execute.""" + + @property + def read_final_analysis_weight_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +class _NoReadMethod: + """Deliberately omit the owner capability.""" + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-analysis-weight-authority-read-v1", + resource_kind="final_analysis_weight_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _adjustment(**overrides: object) -> FinalWeightAdjustmentCoordinate: + values: dict[str, object] = { + "sequence_number": 1, + "adjustment_code": "nonresponse_adjustment", + "method_reference": METHOD_REFERENCE, + "method_version": 2, + "input_weight_artifact_digest": BASE_ARTIFACT_DIGEST, + "output_weight_artifact_digest": ADJUSTED_ARTIFACT_DIGEST, + "configuration_digest": ADJUSTMENT_CONFIG_DIGEST, + "evidence_receipt_digest": ADJUSTMENT_RECEIPT_DIGEST, + "evidence_kind": "nonresponse_adjustment_receipt", + } + values.update(overrides) + return FinalWeightAdjustmentCoordinate(**values) + + +def _record(**overrides: object) -> FinalAnalysisWeightAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": WEIGHT_RECEIPT_REFERENCE, + "analysis_weight_receipt_digest": WEIGHT_RECEIPT_DIGEST, + "evidence_version": 1, + "estimand_reference": ESTIMAND_REFERENCE, + "estimand_digest": ESTIMAND_DIGEST, + "estimand_scope_code": "longitudinal", + "target_population_reference": TARGET_REFERENCE, + "target_population_digest": TARGET_DIGEST, + "analysis_unit_code": "person_occurrence", + "analysis_window_reference": WINDOW_REFERENCE, + "reference_duration_reference": DURATION_REFERENCE, + "reference_duration_digest": DURATION_DIGEST, + "eligible_case_set_digest": ELIGIBLE_CASE_DIGEST, + "analytic_case_occurrence_set_digest": ANALYTIC_CASE_DIGEST, + "source_universe_receipt_reference": SOURCE_REFERENCE, + "source_universe_receipt_version": 4, + "source_universe_receipt_digest": SOURCE_DIGEST, + "sampling_design_receipt_reference": SAMPLING_REFERENCE, + "sampling_design_receipt_version": 3, + "sampling_design_receipt_digest": SAMPLING_DIGEST, + "base_weight_method_code": "inverse_inclusion_probability", + "base_weight_method_version": 1, + "base_weight_evidence_digest": BASE_EVIDENCE_DIGEST, + "base_weight_artifact_digest": BASE_ARTIFACT_DIGEST, + "adjustments": (_adjustment(),), + "final_weight_artifact_digest": ADJUSTED_ARTIFACT_DIGEST, + "weight_eligibility_receipt_reference": ELIGIBILITY_REFERENCE, + "weight_eligibility_receipt_digest": ELIGIBILITY_DIGEST, + "analytic_case_count": 1200, + "constructed_at": CONSTRUCTED_AT, + "correction_sequence": 1, + "supersedes_receipt_digest": None, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return FinalAnalysisWeightAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> FinalAnalysisWeightAuthorityView: + values = dict(_record().fields) + values.update( + { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + ) + values.update(overrides) + return resolve_final_analysis_weight_authority(**values) + + +def test_resolution_binds_complete_estimand_source_base_adjustment_and_final_artifact() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, FinalAnalysisWeightAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["source_universe_receipt_reference"] == SOURCE_REFERENCE + assert port.calls[0]["sampling_design_receipt_version"] == 3 + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert ("estimand_reference", ESTIMAND_REFERENCE) in view.fields + assert ("source_universe_receipt_digest", SOURCE_DIGEST) in view.fields + assert ("base_weight_evidence_digest", BASE_EVIDENCE_DIGEST) in view.fields + assert ("final_weight_artifact_digest", ADJUSTED_ARTIFACT_DIGEST) in view.fields + assert ("owner_contract_released_at", OWNER_RELEASED_AT) in view.fields + assert ("released_at", RELEASED_AT) in view.fields + assert ("superseded_at", None) in view.fields + adjustment = dict(view.fields)["adjustments"][0] + assert tuple(adjustment) == tuple(_adjustment()) + assert adjustment.sequence_number == 1 + assert adjustment.adjustment_code == "nonresponse_adjustment" + assert adjustment.method_reference == METHOD_REFERENCE + assert adjustment.method_version == 2 + assert adjustment.input_weight_artifact_digest == BASE_ARTIFACT_DIGEST + assert adjustment.output_weight_artifact_digest == ADJUSTED_ARTIFACT_DIGEST + assert adjustment.configuration_digest == ADJUSTMENT_CONFIG_DIGEST + assert adjustment.evidence_receipt_digest == ADJUSTMENT_RECEIPT_DIGEST + assert adjustment.evidence_kind == "nonresponse_adjustment_receipt" + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(FinalAnalysisWeightAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"estimand_digest": "a" * 64}, + {"estimand_scope_code": "cross_sectional"}, + {"target_population_digest": "b" * 64}, + {"analysis_unit_code": "household"}, + {"analysis_window_reference": "analysis_window:other"}, + {"reference_duration_digest": "c" * 64}, + {"eligible_case_set_digest": "d" * 64}, + {"analytic_case_occurrence_set_digest": "e" * 64}, + {"source_universe_receipt_version": 5}, + {"source_universe_receipt_digest": "f" * 64}, + {"sampling_design_receipt_version": 4}, + {"sampling_design_receipt_digest": "0" * 64}, + {"base_weight_method_code": "equal_weight"}, + {"base_weight_method_version": 2}, + {"base_weight_evidence_digest": "a" * 64}, + {"weight_eligibility_receipt_digest": "b" * 64}, + {"analytic_case_count": 1199}, + {"constructed_at": CONSTRUCTED_AT + timedelta(seconds=1)}, + {"owner_contract_version": 8}, + {"owner_contract_digest": "c" * 64}, + ], +) +def test_owner_evidence_must_match_every_material_requested_coordinate( + record_overrides: dict[str, object], +) -> None: + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_chain_correction_and_release_chronology_fail_closed() -> None: + with pytest.raises(ValueError): + _record(adjustments=[]) + with pytest.raises(ValueError): + _record(adjustments=(_adjustment(sequence_number=2),)) + with pytest.raises(ValueError): + _record(adjustments=(_adjustment(input_weight_artifact_digest="0" * 64),)) + with pytest.raises(ValueError): + _record(final_weight_artifact_digest="0" * 64) + with pytest.raises(ValueError): + _record(correction_sequence=1, supersedes_receipt_digest=SUPERSEDES_DIGEST) + with pytest.raises(ValueError): + _record(correction_sequence=2, supersedes_receipt_digest=None) + with pytest.raises(ValueError): + _record(correction_sequence=2, supersedes_receipt_digest=WEIGHT_RECEIPT_DIGEST) + corrected = _record(correction_sequence=2, supersedes_receipt_digest=SUPERSEDES_DIGEST) + assert ("correction_sequence", 2) in corrected.fields + with pytest.raises(ValueError): + _record(released_at=CONSTRUCTED_AT - timedelta(seconds=1)) + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + +def test_adjustment_semantics_are_typed_and_no_op_transform_is_rejected() -> None: + with pytest.raises(ValueError): + _adjustment(evidence_kind="generic_weight_evidence") + with pytest.raises(ValueError): + _adjustment(output_weight_artifact_digest=BASE_ARTIFACT_DIGEST) + with pytest.raises(ValueError, match="governed adjustment_code"): + _adjustment( + adjustment_code="custom_transform", + evidence_kind="custom_transform_receipt", + ) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("analysis_weight_receipt_reference", "wrong:receipt", ValueError), + ("analysis_weight_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("evidence_version", 2, ValueError), + ("estimand_reference", "wrong:estimand", ValueError), + ("estimand_digest", "2" * 63, ValueError), + ("estimand_scope_code", "panel", ValueError), + ("target_population_reference", "wrong:population", ValueError), + ("target_population_digest", "3" * 63, ValueError), + ("analysis_unit_code", "Person Occurrence", ValueError), + ("analysis_window_reference", "wrong:window", ValueError), + ("reference_duration_reference", "wrong:duration", ValueError), + ("reference_duration_digest", "4" * 63, ValueError), + ("eligible_case_set_digest", "5" * 63, ValueError), + ("analytic_case_occurrence_set_digest", "6" * 63, ValueError), + ("source_universe_receipt_reference", "wrong:source", ValueError), + ("source_universe_receipt_version", 0, ValueError), + ("source_universe_receipt_digest", "7" * 63, ValueError), + ("sampling_design_receipt_reference", "wrong:sampling", ValueError), + ("sampling_design_receipt_version", 0, ValueError), + ("sampling_design_receipt_digest", "8" * 63, ValueError), + ("base_weight_method_code", "Inverse Probability", ValueError), + ("base_weight_method_version", False, ValueError), + ("base_weight_evidence_digest", "9" * 63, ValueError), + ("base_weight_artifact_digest", "a" * 63, ValueError), + ("adjustments", (_adjustment(), object()), ValueError), + ("final_weight_artifact_digest", "b" * 63, ValueError), + ("weight_eligibility_receipt_reference", "wrong:eligibility", ValueError), + ("weight_eligibility_receipt_digest", "e" * 63, ValueError), + ("analytic_case_count", 0, ValueError), + ("constructed_at", datetime(2026, 9, 17, 8, 0), ValueError), + ("correction_sequence", False, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "f" * 63, ValueError), + ("used_at", datetime(2026, 9, 17, 9, 0), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_adjustment_and_view_are_structurally_immutable() -> None: + adjustment = _adjustment() + with pytest.raises(AttributeError): + object.__setattr__(adjustment, "sequence_number", 2) + + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + assert record.owner_contract_released_at == OWNER_RELEASED_AT + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + with pytest.raises(AttributeError): + object.__setattr__(record, "fields", ()) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + FinalAnalysisWeightAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) diff --git a/services/workforce-validation-api/tests/test_final_analysis_weight_authority_chronology.py b/services/workforce-validation-api/tests/test_final_analysis_weight_authority_chronology.py new file mode 100644 index 000000000..115b04f75 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_analysis_weight_authority_chronology.py @@ -0,0 +1,248 @@ +"""Reject retroactive owner contracts and stale final analysis-weight use.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityIntegrityError, + FinalAnalysisWeightAuthorityRecord, + FinalWeightAdjustmentCoordinate, + resolve_final_analysis_weight_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +WEIGHT_RECEIPT_REFERENCE = "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" +ESTIMAND_REFERENCE = "validation_estimand:criterion-validity-q3" +TARGET_REFERENCE = "analysis_target_population:workers-2026q3" +WINDOW_REFERENCE = "analysis_window:2026q3" +DURATION_REFERENCE = "analysis_reference_duration:2026q3" +SOURCE_REFERENCE = "source_universe_receipt:22222222-2222-4222-8222-222222222222" +SAMPLING_REFERENCE = "sampling_design_receipt:33333333-3333-4333-8333-333333333333" +ELIGIBILITY_REFERENCE = "weight_eligibility_receipt:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +METHOD_REFERENCE = "weight_method:nonresponse-cell-adjustment" +WEIGHT_RECEIPT_DIGEST = "1" * 64 +ESTIMAND_DIGEST = "2" * 64 +TARGET_DIGEST = "3" * 64 +DURATION_DIGEST = "4" * 64 +ELIGIBLE_CASE_DIGEST = "5" * 64 +ANALYTIC_CASE_DIGEST = "6" * 64 +SOURCE_DIGEST = "7" * 64 +SAMPLING_DIGEST = "8" * 64 +BASE_EVIDENCE_DIGEST = "9" * 64 +BASE_ARTIFACT_DIGEST = "a" * 64 +ADJUSTED_ARTIFACT_DIGEST = "b" * 64 +ADJUSTMENT_CONFIG_DIGEST = "c" * 64 +ADJUSTMENT_RECEIPT_DIGEST = "d" * 64 +ELIGIBILITY_DIGEST = "e" * 64 +OWNER_DIGEST = "f" * 64 +CONSTRUCTED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +SUPERSEDED_AT = datetime(2026, 9, 17, 10, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "estimand_reference", + "estimand_digest", + "estimand_scope_code", + "target_population_reference", + "target_population_digest", + "analysis_unit_code", + "analysis_window_reference", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "analytic_case_occurrence_set_digest", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_evidence_digest", + "base_weight_artifact_digest", + "adjustments", + "final_weight_artifact_digest", + "weight_eligibility_receipt_reference", + "weight_eligibility_receipt_digest", + "analytic_case_count", + "constructed_at", + "correction_sequence", + "supersedes_receipt_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + def __init__(self, record: FinalAnalysisWeightAuthorityRecord) -> None: + self.record = record + + def read_final_analysis_weight_authority(self, **_: object) -> FinalAnalysisWeightAuthorityRecord: + return self.record + + +def _adjustment() -> FinalWeightAdjustmentCoordinate: + return FinalWeightAdjustmentCoordinate( + sequence_number=1, + adjustment_code="nonresponse_adjustment", + method_reference=METHOD_REFERENCE, + method_version=2, + input_weight_artifact_digest=BASE_ARTIFACT_DIGEST, + output_weight_artifact_digest=ADJUSTED_ARTIFACT_DIGEST, + configuration_digest=ADJUSTMENT_CONFIG_DIGEST, + evidence_receipt_digest=ADJUSTMENT_RECEIPT_DIGEST, + evidence_kind="nonresponse_adjustment_receipt", + ) + + +def _record(**overrides: object) -> FinalAnalysisWeightAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": WEIGHT_RECEIPT_REFERENCE, + "analysis_weight_receipt_digest": WEIGHT_RECEIPT_DIGEST, + "evidence_version": 1, + "estimand_reference": ESTIMAND_REFERENCE, + "estimand_digest": ESTIMAND_DIGEST, + "estimand_scope_code": "longitudinal", + "target_population_reference": TARGET_REFERENCE, + "target_population_digest": TARGET_DIGEST, + "analysis_unit_code": "person_occurrence", + "analysis_window_reference": WINDOW_REFERENCE, + "reference_duration_reference": DURATION_REFERENCE, + "reference_duration_digest": DURATION_DIGEST, + "eligible_case_set_digest": ELIGIBLE_CASE_DIGEST, + "analytic_case_occurrence_set_digest": ANALYTIC_CASE_DIGEST, + "source_universe_receipt_reference": SOURCE_REFERENCE, + "source_universe_receipt_version": 4, + "source_universe_receipt_digest": SOURCE_DIGEST, + "sampling_design_receipt_reference": SAMPLING_REFERENCE, + "sampling_design_receipt_version": 3, + "sampling_design_receipt_digest": SAMPLING_DIGEST, + "base_weight_method_code": "inverse_inclusion_probability", + "base_weight_method_version": 1, + "base_weight_evidence_digest": BASE_EVIDENCE_DIGEST, + "base_weight_artifact_digest": BASE_ARTIFACT_DIGEST, + "adjustments": (_adjustment(),), + "final_weight_artifact_digest": ADJUSTED_ARTIFACT_DIGEST, + "weight_eligibility_receipt_reference": ELIGIBILITY_REFERENCE, + "weight_eligibility_receipt_digest": ELIGIBILITY_DIGEST, + "analytic_case_count": 1200, + "constructed_at": CONSTRUCTED_AT, + "correction_sequence": 1, + "supersedes_receipt_digest": None, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": SUPERSEDED_AT, + } + values.update(overrides) + return FinalAnalysisWeightAuthorityRecord(**values) + + +def _resolve(*, used_at: datetime, record: FinalAnalysisWeightAuthorityRecord) -> object: + return resolve_final_analysis_weight_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=WEIGHT_RECEIPT_REFERENCE, + analysis_weight_receipt_digest=WEIGHT_RECEIPT_DIGEST, + evidence_version=1, + estimand_reference=ESTIMAND_REFERENCE, + estimand_digest=ESTIMAND_DIGEST, + estimand_scope_code="longitudinal", + target_population_reference=TARGET_REFERENCE, + target_population_digest=TARGET_DIGEST, + analysis_unit_code="person_occurrence", + analysis_window_reference=WINDOW_REFERENCE, + reference_duration_reference=DURATION_REFERENCE, + reference_duration_digest=DURATION_DIGEST, + eligible_case_set_digest=ELIGIBLE_CASE_DIGEST, + analytic_case_occurrence_set_digest=ANALYTIC_CASE_DIGEST, + source_universe_receipt_reference=SOURCE_REFERENCE, + source_universe_receipt_version=4, + source_universe_receipt_digest=SOURCE_DIGEST, + sampling_design_receipt_reference=SAMPLING_REFERENCE, + sampling_design_receipt_version=3, + sampling_design_receipt_digest=SAMPLING_DIGEST, + base_weight_method_code="inverse_inclusion_probability", + base_weight_method_version=1, + base_weight_evidence_digest=BASE_EVIDENCE_DIGEST, + base_weight_artifact_digest=BASE_ARTIFACT_DIGEST, + adjustments=(_adjustment(),), + final_weight_artifact_digest=ADJUSTED_ARTIFACT_DIGEST, + weight_eligibility_receipt_reference=ELIGIBILITY_REFERENCE, + weight_eligibility_receipt_digest=ELIGIBILITY_DIGEST, + analytic_case_count=1200, + constructed_at=CONSTRUCTED_AT, + correction_sequence=1, + supersedes_receipt_digest=None, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-analysis-weight-authority-read-v2", + resource_kind="final_analysis_weight_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(record), + ) + + +def test_chronology_is_owner_evidence_not_caller_input() -> None: + parameters = signature(resolve_final_analysis_weight_authority).parameters + assert "owner_contract_released_at" not in parameters + assert "superseded_at" not in parameters + + +def test_owner_contract_cannot_retroactively_authorize_final_weight() -> None: + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(microseconds=1)) + + +def test_final_weight_uses_owner_resolved_half_open_authority_interval() -> None: + historical = _resolve( + used_at=SUPERSEDED_AT - timedelta(microseconds=1), record=_record() + ) + fields = dict(historical.fields) + assert fields["owner_contract_released_at"] == OWNER_RELEASED_AT + assert fields["superseded_at"] == SUPERSEDED_AT + + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError, match="supersession"): + _resolve(used_at=SUPERSEDED_AT, record=_record()) + + +def test_non_positive_owner_authority_interval_is_rejected() -> None: + with pytest.raises(ValueError, match="superseded_at"): + _record(superseded_at=RELEASED_AT) diff --git a/services/workforce-validation-api/tests/test_final_analysis_weight_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_final_analysis_weight_authority_structural_integrity.py new file mode 100644 index 000000000..270b35d8b --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_analysis_weight_authority_structural_integrity.py @@ -0,0 +1,173 @@ +"""Structural-integrity regressions for final analysis-weight owner evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +import orgmetra_workforce_validation_api.final_weight_authority as target +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityIntegrityError, + FinalAnalysisWeightAuthorityRecord, + FinalWeightAdjustmentCoordinate, + resolve_final_analysis_weight_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +CONSTRUCTED_AT = datetime(2026, 9, 17, 8, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 17, 8, 10, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 8, 30, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc) + + +class _ReadPort: + """Return configured persisted evidence through the final-weight owner capability.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_final_analysis_weight_authority(self, **_: object) -> object: + """Return the configured owner evidence.""" + return self.result + + +def _adjustment() -> FinalWeightAdjustmentCoordinate: + return FinalWeightAdjustmentCoordinate( + sequence_number=1, + adjustment_code="nonresponse_adjustment", + method_reference="weight_method:nonresponse-cell-adjustment", + method_version=2, + input_weight_artifact_digest="a" * 64, + output_weight_artifact_digest="b" * 64, + configuration_digest="c" * 64, + evidence_receipt_digest="d" * 64, + evidence_kind="nonresponse_adjustment_receipt", + ) + + +def _record() -> FinalAnalysisWeightAuthorityRecord: + return FinalAnalysisWeightAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=( + "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" + ), + analysis_weight_receipt_digest="1" * 64, + evidence_version=1, + estimand_reference="validation_estimand:criterion-validity-q3", + estimand_digest="2" * 64, + estimand_scope_code="longitudinal", + target_population_reference="analysis_target_population:workers-2026q3", + target_population_digest="3" * 64, + analysis_unit_code="person_occurrence", + analysis_window_reference="analysis_window:2026q3", + reference_duration_reference="analysis_reference_duration:2026q3", + reference_duration_digest="4" * 64, + eligible_case_set_digest="5" * 64, + analytic_case_occurrence_set_digest="6" * 64, + source_universe_receipt_reference=( + "source_universe_receipt:22222222-2222-4222-8222-222222222222" + ), + source_universe_receipt_version=4, + source_universe_receipt_digest="7" * 64, + sampling_design_receipt_reference=( + "sampling_design_receipt:33333333-3333-4333-8333-333333333333" + ), + sampling_design_receipt_version=3, + sampling_design_receipt_digest="8" * 64, + base_weight_method_code="inverse_inclusion_probability", + base_weight_method_version=1, + base_weight_evidence_digest="9" * 64, + base_weight_artifact_digest="a" * 64, + adjustments=(_adjustment(),), + final_weight_artifact_digest="b" * 64, + weight_eligibility_receipt_reference=( + "weight_eligibility_receipt:44444444-4444-4444-8444-444444444444" + ), + weight_eligibility_receipt_digest="e" * 64, + analytic_case_count=1200, + constructed_at=CONSTRUCTED_AT, + correction_sequence=1, + supersedes_receipt_digest=None, + owner_contract_reference=( + "released_owner_contract:55555555-5555-4555-8555-555555555555" + ), + owner_contract_version=7, + owner_contract_digest="f" * 64, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=None, + ) + + +def _resolve(result: object) -> object: + values = dict(_record().fields) + values.update( + { + "principal": ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-analysis-weight-authority-read-v1", + resource_kind="final_analysis_weight_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=target._READ_FIELDS, + ), + "read_port": _ReadPort(result), + } + ) + return resolve_final_analysis_weight_authority(**values) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + FinalAnalysisWeightAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__(FinalAnalysisWeightAuthorityRecord, tuple(canonical)[:-1]) + + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError): + _resolve(forged) + + +def test_duplicate_nested_field_cannot_be_normalized_away() -> None: + canonical = _record() + duplicate_fields = canonical.fields + ( + ("owner_contract_reference", dict(canonical.fields)["owner_contract_reference"]), + ) + forged = tuple.__new__( + FinalAnalysisWeightAuthorityRecord, + ( + canonical[0], + canonical[1], + duplicate_fields, + canonical[3], + canonical[4], + canonical[5], + ), + ) + + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_final_analysis_weight_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_final_analysis_weight_authority_view_issuance_integrity.py new file mode 100644 index 000000000..b0ea547b2 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_analysis_weight_authority_view_issuance_integrity.py @@ -0,0 +1,77 @@ +"""Regression contract for final analysis-weight view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.final_weight_authority as authority_module +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityIntegrityError, + FinalAnalysisWeightAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_importable_marker_cannot_mint_final_weight_view() -> None: + """Keep the final-weight view seal outside importable module state.""" + marker_name = "_FINAL_ANALYSIS_WEIGHT_VIEW_ISSUANCE_MARKER" + assert not hasattr(authority_module, marker_name) + + forged_view = object.__new__(FinalAnalysisWeightAuthorityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__(forged_view, "_fields", ()) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises(FinalAnalysisWeightAuthorityIntegrityError): + _ = forged_view.fields + + +def test_low_level_tuple_construction_cannot_issue_final_weight_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + FinalAnalysisWeightAuthorityView, + ( + TENANT.int, + STUDY.int, + (("analysis_weight_receipt_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_final_weight_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(FinalAnalysisWeightAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + FinalAnalysisWeightAuthorityIntegrityError, + match="was not issued by resolve_final_analysis_weight_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_final_weight_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(FinalAnalysisWeightAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + FinalAnalysisWeightAuthorityIntegrityError, + match="was not issued by resolve_final_analysis_weight_authority", + ): + _ = forged_view.fields + + +def test_raw_final_weight_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(FinalAnalysisWeightAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_final_analysis_weight_read_port_lookup_contract.py b/services/workforce-validation-api/tests/test_final_analysis_weight_read_port_lookup_contract.py new file mode 100644 index 000000000..5483d9e5c --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_analysis_weight_read_port_lookup_contract.py @@ -0,0 +1,66 @@ +"""Guard the exact immutable lookup key for final analysis-weight owner evidence.""" + +from __future__ import annotations + +from inspect import signature + +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityReadPort, +) + + +def test_final_analysis_weight_read_port_requires_complete_reproducibility_tuple() -> None: + """Require every caller-known coordinate needed to select one owner record.""" + parameters = set( + signature( + FinalAnalysisWeightAuthorityReadPort.read_final_analysis_weight_authority + ).parameters + ) + + required_lookup_coordinates = { + "tenant_record_id", + "validity_study_id", + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "estimand_reference", + "estimand_digest", + "estimand_scope_code", + "target_population_reference", + "target_population_digest", + "analysis_unit_code", + "analysis_window_reference", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "analytic_case_occurrence_set_digest", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_evidence_digest", + "base_weight_artifact_digest", + "adjustments", + "final_weight_artifact_digest", + "weight_eligibility_receipt_reference", + "weight_eligibility_receipt_digest", + "analytic_case_count", + "constructed_at", + "correction_sequence", + "supersedes_receipt_digest", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + } + assert required_lookup_coordinates <= parameters + + owner_resolved_chronology = { + "owner_contract_released_at", + "released_at", + "superseded_at", + } + assert parameters.isdisjoint(owner_resolved_chronology) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_binding_authority.py b/services/workforce-validation-api/tests/test_final_weight_component_binding_authority.py new file mode 100644 index 000000000..4848678af --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_binding_authority.py @@ -0,0 +1,388 @@ +"""Contracts for exact typed-component resolution behind final analysis weights.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +import orgmetra_workforce_validation_api.final_weight_component_binding_authority as target +from orgmetra_workforce_validation_api.final_weight_component_binding_authority import ( + FinalWeightAdjustmentEvidenceBinding, + FinalWeightComponentBindingAuthorityIntegrityError, + FinalWeightComponentBindingAuthorityNotFound, + FinalWeightComponentBindingAuthorityReadPort, + FinalWeightComponentBindingAuthorityRecord, + FinalWeightComponentBindingAuthorityView, + resolve_final_weight_component_binding_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +OWNER_RELEASED_AT = datetime(2026, 9, 19, 4, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 19, 4, 10, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 19, 4, 30, tzinfo=timezone.utc) + + +class _ReadPort: + """Return configured owner evidence and retain the deterministic lookup key.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_final_weight_component_binding_authority(self, **kwargs: object) -> object: + """Return configured evidence after recording caller-known lookup coordinates.""" + self.calls.append(dict(kwargs)) + return self.result + + +class _NoReadMethod: + """Deliberately omit the owner capability.""" + + +class _ProtocolOnly(FinalWeightComponentBindingAuthorityReadPort): + """Inherit only the Protocol placeholder rather than a concrete capability.""" + + +class _DescriptorReadPort: + """Expose an executable descriptor that static capability checks must reject.""" + + @property + def read_final_weight_component_binding_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy( + *, purpose_code: str = "selection_validity_analysis" +) -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-weight-component-binding-read-v1", + resource_kind="final_weight_component_binding_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=target._READ_FIELDS, + ) + + +def _binding( + *, + sequence_number: int = 1, + evidence_kind: str = "nonresponse_adjustment_receipt", + evidence_receipt_reference: str = ( + "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + evidence_version: int = 1, + evidence_receipt_digest: str = "a" * 64, +) -> FinalWeightAdjustmentEvidenceBinding: + return FinalWeightAdjustmentEvidenceBinding( + sequence_number=sequence_number, + evidence_kind=evidence_kind, + evidence_receipt_reference=evidence_receipt_reference, + evidence_version=evidence_version, + evidence_receipt_digest=evidence_receipt_digest, + ) + + +def _record(**overrides: object) -> FinalWeightComponentBindingAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": ( + "analysis_weight_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + "analysis_weight_receipt_digest": "1" * 64, + "analysis_weight_evidence_version": 1, + "binding_reference": ( + "final_weight_component_binding:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + ), + "binding_digest": "2" * 64, + "binding_version": 1, + "base_weight_evidence_receipt_reference": ( + "base_weight_evidence_receipt:cccccccc-cccc-4ccc-8ccc-cccccccccccc" + ), + "base_weight_evidence_receipt_digest": "3" * 64, + "base_weight_evidence_version": 1, + "adjustment_bindings": (_binding(),), + "owner_contract_reference": ( + "released_owner_contract:dddddddd-dddd-4ddd-8ddd-dddddddddddd" + ), + "owner_contract_version": 1, + "owner_contract_digest": "4" * 64, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return FinalWeightComponentBindingAuthorityRecord(**values) + + +def _resolve( + result: object, + *, + used_at: datetime = USED_AT, + read_port: object | None = None, + **overrides: object, +) -> tuple[FinalWeightComponentBindingAuthorityView, object]: + port: object = _ReadPort(result) if read_port is None else read_port + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": ( + "analysis_weight_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + "analysis_weight_receipt_digest": "1" * 64, + "analysis_weight_evidence_version": 1, + "used_at": used_at, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": port, + } + values.update(overrides) + return resolve_final_weight_component_binding_authority(**values), port + + +def test_resolver_returns_exact_component_locator_and_uses_final_receipt_key_only() -> None: + record = _record() + view, port = _resolve(record) + + assert isinstance(port, _ReadPort) + assert isinstance(port, FinalWeightComponentBindingAuthorityReadPort) + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["base_weight_evidence_receipt_reference"] == ( + "base_weight_evidence_receipt:cccccccc-cccc-4ccc-8ccc-cccccccccccc" + ) + bindings = fields["adjustment_bindings"] + assert type(bindings) is tuple + assert bindings[0].evidence_receipt_reference == ( + "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ) + assert fields["owner_contract_released_at"] == OWNER_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert fields["superseded_at"] is None + assert port.calls == [ + { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": ( + "analysis_weight_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + "analysis_weight_receipt_digest": "1" * 64, + "analysis_weight_evidence_version": 1, + } + ] + + +def test_binding_sequences_must_be_contiguous_after_governed_adjustment_admission() -> None: + bindings = ( + _binding(), + _binding( + sequence_number=3, + evidence_kind="trimming_bounding_adjustment_receipt", + evidence_receipt_reference=( + "trimming_bounding_adjustment_receipt:22222222-2222-4222-8222-222222222222" + ), + evidence_receipt_digest="b" * 64, + ), + ) + with pytest.raises(ValueError, match="contiguous"): + _record(adjustment_bindings=bindings) + + with pytest.raises(ValueError, match="contiguous"): + _record(adjustment_bindings=(bindings[1], bindings[0])) + + +@pytest.mark.parametrize( + ("overrides", "message"), + [ + ({"analysis_weight_evidence_version": 2}, "analysis_weight_evidence_version"), + ({"binding_version": 2}, "binding_version"), + ({"base_weight_evidence_version": 2}, "base_weight_evidence_version"), + ({"adjustment_bindings": [_binding()]}, "immutable tuple"), + ({"adjustment_bindings": (object(),)}, "exact FinalWeightAdjustmentEvidenceBinding"), + ( + {"owner_contract_released_at": RELEASED_AT + timedelta(seconds=1)}, + "owner contract", + ), + ({"superseded_at": RELEASED_AT}, "superseded_at"), + ], +) +def test_record_rejects_noncanonical_contract_shapes( + overrides: dict[str, object], message: str +) -> None: + with pytest.raises(ValueError, match=message): + _record(**overrides) + + +@pytest.mark.parametrize( + ("kind", "reference"), + [ + ( + "unknown_adjustment_receipt", + "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111", + ), + ( + "calibration_adjustment_receipt", + "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111", + ), + ], +) +def test_adjustment_binding_rejects_unknown_kind_or_wrong_receipt_namespace( + kind: str, reference: str +) -> None: + with pytest.raises(ValueError): + _binding(evidence_kind=kind, evidence_receipt_reference=reference) + + +def test_adjustment_binding_rejects_non_v1_evidence() -> None: + with pytest.raises(ValueError, match="evidence_version"): + _binding(evidence_version=2) + + +def test_forged_nested_binding_cannot_hide_trailing_state() -> None: + canonical = _binding() + forged = tuple.__new__( + FinalWeightAdjustmentEvidenceBinding, + tuple(canonical) + ("hidden-component-coordinate",), + ) + with pytest.raises(ValueError, match="canonical"): + _record(adjustment_bindings=(forged,)) + + +def test_forged_outer_record_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + FinalWeightComponentBindingAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + with pytest.raises(FinalWeightComponentBindingAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_outer_record_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + FinalWeightComponentBindingAuthorityRecord, + tuple(canonical)[:-1], + ) + with pytest.raises(FinalWeightComponentBindingAuthorityIntegrityError): + _resolve(forged) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + { + "analysis_weight_receipt_reference": ( + "analysis_weight_receipt:eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee" + ) + }, + {"analysis_weight_receipt_digest": "f" * 64}, + ], +) +def test_owner_binding_must_target_the_exact_final_weight_receipt( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(FinalWeightComponentBindingAuthorityIntegrityError, match="targets"): + _resolve(_record(**record_overrides)) + + +def test_authority_interval_fails_closed_before_release_and_at_cutover() -> None: + with pytest.raises(FinalWeightComponentBindingAuthorityIntegrityError, match="before"): + _resolve(_record(), used_at=RELEASED_AT - timedelta(microseconds=1)) + cutover = USED_AT + record = _record(superseded_at=cutover) + assert record.released_at == RELEASED_AT + assert record.owner_contract_released_at == OWNER_RELEASED_AT + assert record.superseded_at == cutover + with pytest.raises(FinalWeightComponentBindingAuthorityIntegrityError, match="supersession"): + _resolve(record, used_at=cutover) + + +def test_authorization_denial_occurs_before_owner_read() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(_record(), read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_not_found_and_noncanonical_owner_types_fail_closed() -> None: + with pytest.raises(FinalWeightComponentBindingAuthorityNotFound): + _resolve(None) + with pytest.raises(FinalWeightComponentBindingAuthorityIntegrityError, match="non-canonical"): + _resolve(object()) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("analysis_weight_receipt_reference", "wrong:receipt", ValueError), + ("analysis_weight_receipt_digest", "ABC", ValueError), + ("analysis_weight_evidence_version", 2, ValueError), + ("used_at", datetime(2026, 9, 19, 4, 30), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_read( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(_record(), read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + with pytest.raises(AttributeError): + object.__setattr__(record, "fields", ()) + + view, _ = _resolve(record) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError, match="issued only"): + FinalWeightComponentBindingAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_binding_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_final_weight_component_binding_authority_view_issuance_integrity.py new file mode 100644 index 000000000..a13018cb4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_binding_authority_view_issuance_integrity.py @@ -0,0 +1,77 @@ +"""Regression contract for final-weight component-binding view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.final_weight_component_binding_authority as authority_module +from orgmetra_workforce_validation_api.final_weight_component_binding_authority import ( + FinalWeightComponentBindingAuthorityIntegrityError, + FinalWeightComponentBindingAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_importable_marker_cannot_mint_component_binding_view() -> None: + """Keep the component-binding view seal outside importable module state.""" + marker_name = "_FINAL_WEIGHT_COMPONENT_BINDING_VIEW_ISSUANCE_MARKER" + assert not hasattr(authority_module, marker_name) + + forged_view = object.__new__(FinalWeightComponentBindingAuthorityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__(forged_view, "_fields", ()) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises(FinalWeightComponentBindingAuthorityIntegrityError): + _ = forged_view.fields + + +def test_low_level_tuple_construction_cannot_issue_component_binding_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + FinalWeightComponentBindingAuthorityView, + ( + TENANT.int, + STUDY.int, + (("binding_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_component_binding_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(FinalWeightComponentBindingAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + FinalWeightComponentBindingAuthorityIntegrityError, + match="was not issued by resolve_final_weight_component_binding_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_component_binding_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(FinalWeightComponentBindingAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + FinalWeightComponentBindingAuthorityIntegrityError, + match="was not issued by resolve_final_weight_component_binding_authority", + ): + _ = forged_view.fields + + +def test_raw_component_binding_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(FinalWeightComponentBindingAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_final_weight_component_binding_sequence_contiguity.py b/services/workforce-validation-api/tests/test_final_weight_component_binding_sequence_contiguity.py new file mode 100644 index 000000000..72ad8f409 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_binding_sequence_contiguity.py @@ -0,0 +1,98 @@ +"""Regression contract for contiguous final-weight component receipt bindings.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.final_weight_component_binding_authority import ( + FinalWeightAdjustmentEvidenceBinding, + FinalWeightComponentBindingAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +OWNER_RELEASED_AT = datetime(2026, 9, 19, 4, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 19, 4, 10, tzinfo=timezone.utc) + + +def _binding( + *, + sequence_number: int, + evidence_kind: str, + evidence_receipt_reference: str, + evidence_receipt_digest: str, +) -> FinalWeightAdjustmentEvidenceBinding: + return FinalWeightAdjustmentEvidenceBinding( + sequence_number=sequence_number, + evidence_kind=evidence_kind, + evidence_receipt_reference=evidence_receipt_reference, + evidence_version=1, + evidence_receipt_digest=evidence_receipt_digest, + ) + + +def _record( + adjustment_bindings: tuple[FinalWeightAdjustmentEvidenceBinding, ...], +) -> FinalWeightComponentBindingAuthorityRecord: + return FinalWeightComponentBindingAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=( + "analysis_weight_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + analysis_weight_receipt_digest="1" * 64, + analysis_weight_evidence_version=1, + binding_reference=( + "final_weight_component_binding:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + ), + binding_digest="2" * 64, + binding_version=1, + base_weight_evidence_receipt_reference=( + "base_weight_evidence_receipt:cccccccc-cccc-4ccc-8ccc-cccccccccccc" + ), + base_weight_evidence_receipt_digest="3" * 64, + base_weight_evidence_version=1, + adjustment_bindings=adjustment_bindings, + owner_contract_reference=( + "released_owner_contract:dddddddd-dddd-4ddd-8ddd-dddddddddddd" + ), + owner_contract_version=1, + owner_contract_digest="4" * 64, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def test_component_binding_sequences_must_start_at_one_and_remain_contiguous() -> None: + first = _binding( + sequence_number=1, + evidence_kind="nonresponse_adjustment_receipt", + evidence_receipt_reference=( + "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + evidence_receipt_digest="a" * 64, + ) + third = _binding( + sequence_number=3, + evidence_kind="trimming_bounding_adjustment_receipt", + evidence_receipt_reference=( + "trimming_bounding_adjustment_receipt:33333333-3333-4333-8333-333333333333" + ), + evidence_receipt_digest="b" * 64, + ) + second = _binding( + sequence_number=2, + evidence_kind="calibration_adjustment_receipt", + evidence_receipt_reference=( + "calibration_adjustment_receipt:22222222-2222-4222-8222-222222222222" + ), + evidence_receipt_digest="c" * 64, + ) + + with pytest.raises(ValueError, match="contiguous"): + _record((first, third)) + with pytest.raises(ValueError, match="contiguous"): + _record((second,)) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_authorization.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_authorization.py new file mode 100644 index 000000000..fdbee7464 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_authorization.py @@ -0,0 +1,138 @@ +"""Purpose-bound authorization for final-weight component owner reads.""" + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + corroborate_final_weight_component_evidence, +) +from test_final_weight_component_evidence_resolution import ( + FINAL_REFERENCE, + READ_FIELDS, + TENANT, + USED_AT, + _ReadPort, + _binding, + _final_weight, +) + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-weight-component-evidence-resolution-read-v1", + resource_kind="final_weight_component_evidence_resolution", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def test_denied_purpose_stops_before_any_component_owner_read() -> None: + """Reject a purpose mismatch and retain the exact final-receipt audit target.""" + port = _ReadPort() + + with pytest.raises(AuthorizationDeniedError) as exc_info: + corroborate_final_weight_component_evidence( + principal=_principal(), + final_weight=_final_weight(), + binding=_binding(), + used_at=USED_AT, + purpose_code="compensation_administration", + policy=_policy(), + read_port=port, + ) + + receipt_tail = FINAL_REFERENCE.partition(":")[2] + assert exc_info.value.decision.resource_reference == ( + f"final_weight_component_evidence_resolution:{receipt_tail}" + ) + assert port.final_owner_calls == [] + assert port.binding_owner_calls == [] + assert port.base_calls == [] + assert port.adjustment_calls == [] + + +def test_cross_tenant_principal_stops_before_any_component_owner_read() -> None: + """Reject an actor from another tenant before component evidence is exposed.""" + port = _ReadPort() + + with pytest.raises(AuthorizationDeniedError): + corroborate_final_weight_component_evidence( + principal=_principal( + tenant_record_id=UUID("10000000-0000-7000-8000-000000000002") + ), + final_weight=_final_weight(), + binding=_binding(), + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=port, + ) + + assert port.final_owner_calls == [] + assert port.binding_owner_calls == [] + assert port.base_calls == [] + assert port.adjustment_calls == [] + + +@pytest.mark.parametrize("invalid_principal", [object(), None]) +def test_noncanonical_principal_fails_before_component_owner_read( + invalid_principal: object, +) -> None: + """Require an exact principal runtime type before any native owner access.""" + port = _ReadPort() + + with pytest.raises(TypeError, match="exact ValidationPrincipal"): + corroborate_final_weight_component_evidence( + principal=invalid_principal, # type: ignore[arg-type] + final_weight=_final_weight(), + binding=_binding(), + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=port, + ) + + assert port.final_owner_calls == [] + assert port.binding_owner_calls == [] + assert port.base_calls == [] + assert port.adjustment_calls == [] + + +@pytest.mark.parametrize("invalid_policy", [object(), None]) +def test_noncanonical_policy_fails_before_component_owner_read( + invalid_policy: object, +) -> None: + """Require an exact policy runtime type before any native owner access.""" + port = _ReadPort() + + with pytest.raises(TypeError, match="exact PurposeBoundAccessPolicy"): + corroborate_final_weight_component_evidence( + principal=_principal(), + final_weight=_final_weight(), + binding=_binding(), + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=invalid_policy, # type: ignore[arg-type] + read_port=port, + ) + + assert port.final_owner_calls == [] + assert port.binding_owner_calls == [] + assert port.base_calls == [] + assert port.adjustment_calls == [] diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_field_minimization.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_field_minimization.py new file mode 100644 index 000000000..671179644 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_field_minimization.py @@ -0,0 +1,170 @@ +"""Field-minimized authorization for final-weight component evidence reads.""" + +from __future__ import annotations + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + corroborate_final_weight_component_evidence, +) +from test_final_weight_component_evidence_resolution import ( + BASE_ARTIFACT_DIGEST, + TENANT, + USED_AT, + _ReadPort, + _binding, + _final_weight, + _principal, +) + +OWNER_PROVENANCE_READ_FIELDS = frozenset( + { + "tenant_record_id", + "validity_study_id", + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "analysis_weight_evidence_version", + "evidence_version", + "estimand_reference", + "estimand_digest", + "estimand_scope_code", + "target_population_reference", + "target_population_digest", + "analysis_unit_code", + "analysis_window_reference", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "analytic_case_occurrence_set_digest", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_evidence_digest", + "base_weight_artifact_digest", + "adjustments", + "final_weight_artifact_digest", + "weight_eligibility_receipt_reference", + "weight_eligibility_receipt_digest", + "analytic_case_count", + "constructed_at", + "correction_sequence", + "supersedes_receipt_digest", + "binding_reference", + "binding_digest", + "binding_version", + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "base_weight_evidence_version", + "adjustment_bindings", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) +BASE_COMPONENT_READ_FIELDS = frozenset( + { + "tenant_record_id", + "validity_study_id", + "receipt_reference", + "receipt_digest", + "evidence_version", + "method_code", + "method_version", + "output_weight_artifact_digest", + "released_at", + "superseded_at", + } +) +BASE_ONLY_READ_FIELDS = OWNER_PROVENANCE_READ_FIELDS | BASE_COMPONENT_READ_FIELDS + + +def _base_only_policy(*, permitted_fields: frozenset[str]) -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-weight-component-evidence-resolution-base-only-v1", + resource_kind="final_weight_component_evidence_resolution", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=permitted_fields, + ) + + +def _corroborate_base_only(*, policy: PurposeBoundAccessPolicy, port: _ReadPort): + final_record = _final_weight( + adjustments=(), + final_weight_artifact_digest=BASE_ARTIFACT_DIGEST, + ) + binding_record = _binding(adjustment_bindings=()) + port.final_owner = final_record + port.binding_owner = binding_record + return corroborate_final_weight_component_evidence( + principal=_principal(), + final_weight=final_record, + binding=binding_record, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=port, + ) + + +def test_base_only_resolution_does_not_request_adjustment_only_policy_fields() -> None: + """Authorize owner provenance plus base projection fields when no adjustment owner is read.""" + port = _ReadPort() + + resolution = _corroborate_base_only( + policy=_base_only_policy(permitted_fields=BASE_ONLY_READ_FIELDS), + port=port, + ) + + assert resolution.adjustments == () + assert len(port.final_owner_calls) == 1 + assert len(port.binding_owner_calls) == 1 + assert len(port.base_calls) == 1 + assert port.adjustment_calls == [] + + +def test_component_only_policy_cannot_authorize_owner_provenance_reads() -> None: + """Deny before all owner access when final/binding provenance fields are not permitted.""" + port = _ReadPort() + + with pytest.raises(AuthorizationDeniedError): + _corroborate_base_only( + policy=_base_only_policy(permitted_fields=BASE_COMPONENT_READ_FIELDS), + port=port, + ) + + assert port.final_owner_calls == [] + assert port.binding_owner_calls == [] + assert port.base_calls == [] + assert port.adjustment_calls == [] + + +@pytest.mark.parametrize("omitted_scope_field", ["tenant_record_id", "validity_study_id"]) +def test_base_only_resolution_authorizes_native_owner_scope_fields_before_read( + omitted_scope_field: str, +) -> None: + """Deny before owner access when policy omits a scope field consumed from the projection.""" + port = _ReadPort() + permitted_fields = BASE_ONLY_READ_FIELDS - {omitted_scope_field} + + with pytest.raises(AuthorizationDeniedError): + _corroborate_base_only( + policy=_base_only_policy(permitted_fields=permitted_fields), + port=port, + ) + + assert port.final_owner_calls == [] + assert port.binding_owner_calls == [] + assert port.base_calls == [] + assert port.adjustment_calls == [] diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_issuance_marker_integrity.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_issuance_marker_integrity.py new file mode 100644 index 000000000..3d219d849 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_issuance_marker_integrity.py @@ -0,0 +1,63 @@ +"""Hostile issuance-marker and private-issuer edge cases for component evidence.""" + +from __future__ import annotations + +import pytest + +import orgmetra_workforce_validation_api.final_weight_component_evidence_resolution as resolution_module +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + FinalWeightComponentEvidenceIntegrityError, + FinalWeightComponentEvidenceResolution, +) +from test_final_weight_component_evidence_resolution_low_level_issuance import _base_evidence + + +def test_resolution_module_exposes_no_issuance_marker_capability() -> None: + """Keep proof-result sealing authority out of ordinary module state.""" + assert not hasattr(resolution_module, "_RESOLUTION_ISSUANCE_MARKER") + + +def test_importable_marker_cannot_mint_proof_bearing_resolution() -> None: + """Reject caller-populated proof slots even when a module marker is obtainable.""" + forged = object.__new__(FinalWeightComponentEvidenceResolution) + object.__setattr__( + forged, + "_FinalWeightComponentEvidenceResolution__base_weight", + _base_evidence(), + ) + object.__setattr__( + forged, + "_FinalWeightComponentEvidenceResolution__adjustments", + (), + ) + caller_marker = getattr(resolution_module, "_RESOLUTION_ISSUANCE_MARKER", object()) + object.__setattr__( + forged, + "_FinalWeightComponentEvidenceResolution__issuance_marker", + caller_marker, + ) + + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="not issued"): + _ = forged.base_weight + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="not issued"): + _ = forged.adjustments + + +def test_wrong_private_issuance_marker_cannot_expose_proof_properties() -> None: + """Fail closed when hostile allocation populates a marker that is not the canonical seal.""" + forged = object.__new__(FinalWeightComponentEvidenceResolution) + object.__setattr__( + forged, + "_FinalWeightComponentEvidenceResolution__issuance_marker", + object(), + ) + + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="not issued"): + _ = forged.base_weight + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="not issued"): + _ = forged.adjustments + + +def test_resolution_module_exposes_no_generic_private_issuer() -> None: + """Keep generic proof-result minting outside ordinary module state.""" + assert not hasattr(resolution_module, "_issue_component_evidence_resolution") diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_owner_provenance.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_owner_provenance.py new file mode 100644 index 000000000..91cc73f96 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_owner_provenance.py @@ -0,0 +1,82 @@ +"""Authoritative owner provenance for final-weight component corroboration.""" + +from __future__ import annotations + +import pytest + +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + FinalWeightComponentEvidenceIntegrityError, + FinalWeightComponentEvidenceNotFound, + corroborate_final_weight_component_evidence, +) +from test_final_weight_component_evidence_resolution import ( + USED_AT, + _ReadPort, + _binding, + _final_weight, + _policy, + _principal, +) + + +class _OwnerProvenanceReadPort(_ReadPort): + """Expose independent final-weight and binding owner reads before component reads.""" + + def __init__(self, *, final_owner: object, binding_owner: object) -> None: + super().__init__() + self.final_owner = final_owner + self.binding_owner = binding_owner + self.final_owner_calls: list[dict[str, object]] = [] + self.binding_owner_calls: list[dict[str, object]] = [] + + def read_final_analysis_weight_authority(self, **kwargs: object): + """Return owner-confirmed final-weight authority or an explicit miss.""" + self.final_owner_calls.append(dict(kwargs)) + return self.final_owner + + def read_final_weight_component_binding_authority(self, **kwargs: object): + """Return owner-confirmed component-binding authority or an explicit miss.""" + self.binding_owner_calls.append(dict(kwargs)) + return self.binding_owner + + +def _corroborate_with(port: _OwnerProvenanceReadPort) -> None: + corroborate_final_weight_component_evidence( + principal=_principal(), + final_weight=_final_weight(), + binding=_binding(), + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=port, + ) + + +def test_missing_final_weight_owner_authority_stops_before_component_reads() -> None: + """Canonical caller records cannot substitute for released final-weight owner truth.""" + port = _OwnerProvenanceReadPort(final_owner=None, binding_owner=_binding()) + + with pytest.raises(FinalWeightComponentEvidenceNotFound, match="final-weight"): + _corroborate_with(port) + + assert len(port.final_owner_calls) == 1 + assert port.binding_owner_calls == [] + assert port.base_calls == [] + assert port.adjustment_calls == [] + + +def test_conflicting_binding_owner_authority_stops_before_component_reads() -> None: + """A locally canonical binding must exactly match the native owner record.""" + conflicting_binding = _binding(binding_digest="b" * 64) + port = _OwnerProvenanceReadPort( + final_owner=_final_weight(), + binding_owner=conflicting_binding, + ) + + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="binding owner"): + _corroborate_with(port) + + assert len(port.final_owner_calls) == 1 + assert len(port.binding_owner_calls) == 1 + assert port.base_calls == [] + assert port.adjustment_calls == [] diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_public_surface.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_public_surface.py new file mode 100644 index 000000000..585638f21 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_public_surface.py @@ -0,0 +1,23 @@ +"""Public package contract for deterministic final-weight component corroboration.""" + +from __future__ import annotations + +import orgmetra_workforce_validation_api as api + + +def test_component_evidence_resolution_is_public_package_surface() -> None: + expected = { + "AdjustmentComponentEvidence", + "BaseWeightComponentEvidence", + "FinalWeightComponentEvidenceIntegrityError", + "FinalWeightComponentEvidenceNotFound", + "FinalWeightComponentEvidenceReadPort", + "FinalWeightComponentEvidenceResolution", + "corroborate_final_weight_component_evidence", + } + + assert expected <= set(api.__all__) + for name in expected: + assert getattr(api, name).__module__ == ( + "orgmetra_workforce_validation_api.final_weight_component_evidence_resolution" + ) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution.py new file mode 100644 index 000000000..3d4e9e894 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution.py @@ -0,0 +1,436 @@ +"""Cross-owner consistency for deterministic final-weight component reproduction.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityRecord, + FinalWeightAdjustmentCoordinate, +) +from orgmetra_workforce_validation_api.final_weight_component_binding_authority import ( + FinalWeightAdjustmentEvidenceBinding, + FinalWeightComponentBindingAuthorityRecord, +) +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + AdjustmentComponentEvidence, + BaseWeightComponentEvidence, + FinalWeightComponentEvidenceIntegrityError, + FinalWeightComponentEvidenceNotFound, + FinalWeightComponentEvidenceResolution, + corroborate_final_weight_component_evidence, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000f2") +CONSTRUCTED_AT = datetime(2026, 9, 19, 4, 5, tzinfo=timezone.utc) +FINAL_RELEASED_AT = datetime(2026, 9, 19, 4, 10, tzinfo=timezone.utc) +BINDING_RELEASED_AT = datetime(2026, 9, 19, 4, 15, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 19, 4, 30, tzinfo=timezone.utc) +FINAL_REFERENCE = "analysis_weight_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +FINAL_DIGEST = "1" * 64 +BASE_RECEIPT_REFERENCE = ( + "base_weight_evidence_receipt:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +) +BASE_RECEIPT_DIGEST = "2" * 64 +BASE_ARTIFACT_DIGEST = "3" * 64 +ADJUSTMENT_RECEIPT_REFERENCE = ( + "nonresponse_adjustment_receipt:cccccccc-cccc-4ccc-8ccc-cccccccccccc" +) +ADJUSTMENT_RECEIPT_DIGEST = "4" * 64 +ADJUSTMENT_OUTPUT_DIGEST = "5" * 64 +CONFIGURATION_DIGEST = "6" * 64 +METHOD_REFERENCE = "weight_method:dddddddd-dddd-4ddd-8ddd-dddddddddddd" +OWNER_PROVENANCE_READ_FIELDS = frozenset( + { + "tenant_record_id", + "validity_study_id", + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "analysis_weight_evidence_version", + "evidence_version", + "estimand_reference", + "estimand_digest", + "estimand_scope_code", + "target_population_reference", + "target_population_digest", + "analysis_unit_code", + "analysis_window_reference", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "analytic_case_occurrence_set_digest", + "source_universe_receipt_reference", + "source_universe_receipt_version", + "source_universe_receipt_digest", + "sampling_design_receipt_reference", + "sampling_design_receipt_version", + "sampling_design_receipt_digest", + "base_weight_method_code", + "base_weight_method_version", + "base_weight_evidence_digest", + "base_weight_artifact_digest", + "adjustments", + "final_weight_artifact_digest", + "weight_eligibility_receipt_reference", + "weight_eligibility_receipt_digest", + "analytic_case_count", + "constructed_at", + "correction_sequence", + "supersedes_receipt_digest", + "binding_reference", + "binding_digest", + "binding_version", + "base_weight_evidence_receipt_reference", + "base_weight_evidence_receipt_digest", + "base_weight_evidence_version", + "adjustment_bindings", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) +COMPONENT_READ_FIELDS = frozenset( + { + "tenant_record_id", + "validity_study_id", + "receipt_reference", + "receipt_digest", + "evidence_version", + "method_code", + "method_reference", + "method_version", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "configuration_digest", + "evidence_kind", + "released_at", + "superseded_at", + } +) +READ_FIELDS = OWNER_PROVENANCE_READ_FIELDS | COMPONENT_READ_FIELDS + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-weight-component-evidence-resolution-read-v1", + resource_kind="final_weight_component_evidence_resolution", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _final_weight(**overrides: object) -> FinalAnalysisWeightAuthorityRecord: + adjustment = FinalWeightAdjustmentCoordinate( + sequence_number=1, + adjustment_code="nonresponse_adjustment", + method_reference=METHOD_REFERENCE, + method_version=1, + input_weight_artifact_digest=BASE_ARTIFACT_DIGEST, + output_weight_artifact_digest=ADJUSTMENT_OUTPUT_DIGEST, + configuration_digest=CONFIGURATION_DIGEST, + evidence_receipt_digest=ADJUSTMENT_RECEIPT_DIGEST, + evidence_kind="nonresponse_adjustment_receipt", + ) + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": FINAL_REFERENCE, + "analysis_weight_receipt_digest": FINAL_DIGEST, + "evidence_version": 1, + "estimand_reference": "validation_estimand:11111111-1111-4111-8111-111111111111", + "estimand_digest": "7" * 64, + "estimand_scope_code": "cross_sectional", + "target_population_reference": ( + "analysis_target_population:22222222-2222-4222-8222-222222222222" + ), + "target_population_digest": "8" * 64, + "analysis_unit_code": "person", + "analysis_window_reference": "analysis_window:33333333-3333-4333-8333-333333333333", + "reference_duration_reference": ( + "analysis_reference_duration:44444444-4444-4444-8444-444444444444" + ), + "reference_duration_digest": "9" * 64, + "eligible_case_set_digest": "a" * 64, + "analytic_case_occurrence_set_digest": "b" * 64, + "source_universe_receipt_reference": ( + "source_universe_receipt:55555555-5555-4555-8555-555555555555" + ), + "source_universe_receipt_version": 1, + "source_universe_receipt_digest": "c" * 64, + "sampling_design_receipt_reference": ( + "sampling_design_receipt:66666666-6666-4666-8666-666666666666" + ), + "sampling_design_receipt_version": 1, + "sampling_design_receipt_digest": "d" * 64, + "base_weight_method_code": "inverse_probability", + "base_weight_method_version": 1, + "base_weight_evidence_digest": BASE_RECEIPT_DIGEST, + "base_weight_artifact_digest": BASE_ARTIFACT_DIGEST, + "adjustments": (adjustment,), + "final_weight_artifact_digest": ADJUSTMENT_OUTPUT_DIGEST, + "weight_eligibility_receipt_reference": ( + "weight_eligibility_receipt:77777777-7777-4777-8777-777777777777" + ), + "weight_eligibility_receipt_digest": "e" * 64, + "analytic_case_count": 10, + "constructed_at": CONSTRUCTED_AT, + "correction_sequence": 1, + "supersedes_receipt_digest": None, + "owner_contract_reference": ( + "released_owner_contract:88888888-8888-4888-8888-888888888888" + ), + "owner_contract_version": 1, + "owner_contract_digest": "f" * 64, + "owner_contract_released_at": CONSTRUCTED_AT - timedelta(minutes=10), + "released_at": FINAL_RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return FinalAnalysisWeightAuthorityRecord(**values) + + +def _binding(**overrides: object) -> FinalWeightComponentBindingAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": FINAL_REFERENCE, + "analysis_weight_receipt_digest": FINAL_DIGEST, + "analysis_weight_evidence_version": 1, + "binding_reference": ( + "final_weight_component_binding:99999999-9999-4999-8999-999999999999" + ), + "binding_digest": "0" * 64, + "binding_version": 1, + "base_weight_evidence_receipt_reference": BASE_RECEIPT_REFERENCE, + "base_weight_evidence_receipt_digest": BASE_RECEIPT_DIGEST, + "base_weight_evidence_version": 1, + "adjustment_bindings": ( + FinalWeightAdjustmentEvidenceBinding( + sequence_number=1, + evidence_kind="nonresponse_adjustment_receipt", + evidence_receipt_reference=ADJUSTMENT_RECEIPT_REFERENCE, + evidence_version=1, + evidence_receipt_digest=ADJUSTMENT_RECEIPT_DIGEST, + ), + ), + "owner_contract_reference": ( + "released_owner_contract:aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee" + ), + "owner_contract_version": 1, + "owner_contract_digest": "a" * 64, + "owner_contract_released_at": FINAL_RELEASED_AT, + "released_at": BINDING_RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return FinalWeightComponentBindingAuthorityRecord(**values) + + +def _base_evidence(**overrides: object) -> BaseWeightComponentEvidence: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "receipt_reference": BASE_RECEIPT_REFERENCE, + "receipt_digest": BASE_RECEIPT_DIGEST, + "evidence_version": 1, + "method_code": "inverse_probability", + "method_version": 1, + "output_weight_artifact_digest": BASE_ARTIFACT_DIGEST, + "released_at": CONSTRUCTED_AT - timedelta(minutes=15), + "superseded_at": None, + } + values.update(overrides) + return BaseWeightComponentEvidence(**values) + + +def _adjustment_evidence(**overrides: object) -> AdjustmentComponentEvidence: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "evidence_kind": "nonresponse_adjustment_receipt", + "receipt_reference": ADJUSTMENT_RECEIPT_REFERENCE, + "receipt_digest": ADJUSTMENT_RECEIPT_DIGEST, + "evidence_version": 1, + "method_reference": METHOD_REFERENCE, + "method_version": 1, + "input_weight_artifact_digest": BASE_ARTIFACT_DIGEST, + "output_weight_artifact_digest": ADJUSTMENT_OUTPUT_DIGEST, + "configuration_digest": CONFIGURATION_DIGEST, + "released_at": CONSTRUCTED_AT - timedelta(minutes=1), + "superseded_at": None, + } + values.update(overrides) + return AdjustmentComponentEvidence(**values) + + +class _ReadPort: + def __init__( + self, + *, + base: BaseWeightComponentEvidence | None = None, + adjustment: AdjustmentComponentEvidence | None = None, + ) -> None: + self.base = _base_evidence() if base is None else base + self.adjustment = _adjustment_evidence() if adjustment is None else adjustment + self.final_owner = _final_weight() + self.binding_owner = _binding() + self.final_owner_calls: list[dict[str, object]] = [] + self.binding_owner_calls: list[dict[str, object]] = [] + self.base_calls: list[dict[str, object]] = [] + self.adjustment_calls: list[dict[str, object]] = [] + + def read_final_analysis_weight_authority( + self, **kwargs: object + ) -> FinalAnalysisWeightAuthorityRecord | None: + self.final_owner_calls.append(dict(kwargs)) + return self.final_owner + + def read_final_weight_component_binding_authority( + self, **kwargs: object + ) -> FinalWeightComponentBindingAuthorityRecord | None: + self.binding_owner_calls.append(dict(kwargs)) + return self.binding_owner + + def read_base_weight_component_evidence( + self, **kwargs: object + ) -> BaseWeightComponentEvidence | None: + self.base_calls.append(dict(kwargs)) + return self.base + + def read_adjustment_component_evidence( + self, **kwargs: object + ) -> AdjustmentComponentEvidence | None: + self.adjustment_calls.append(dict(kwargs)) + return self.adjustment + + +def _corroborate( + *, + read_port: object, + final_weight: FinalAnalysisWeightAuthorityRecord | None = None, + binding: FinalWeightComponentBindingAuthorityRecord | None = None, + used_at: datetime = USED_AT, +) -> FinalWeightComponentEvidenceResolution: + final_record = _final_weight() if final_weight is None else final_weight + binding_record = _binding() if binding is None else binding + if isinstance(read_port, _ReadPort): + read_port.final_owner = final_record + read_port.binding_owner = binding_record + return corroborate_final_weight_component_evidence( + principal=_principal(), + final_weight=final_record, + binding=binding_record, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=read_port, + ) + + +def test_exact_receipt_identity_resolves_and_cross_checks_component_semantics() -> None: + port = _ReadPort() + resolution = _corroborate(read_port=port) + + assert isinstance(resolution, FinalWeightComponentEvidenceResolution) + assert resolution.base_weight.tenant_record_id == TENANT + assert resolution.base_weight.validity_study_id == STUDY + assert resolution.base_weight.receipt_reference == BASE_RECEIPT_REFERENCE + assert resolution.adjustments == (_adjustment_evidence(),) + assert len(port.final_owner_calls) == 1 + assert len(port.binding_owner_calls) == 1 + assert port.base_calls == [ + { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "receipt_reference": BASE_RECEIPT_REFERENCE, + "receipt_digest": BASE_RECEIPT_DIGEST, + "evidence_version": 1, + } + ] + assert port.adjustment_calls == [ + { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "evidence_kind": "nonresponse_adjustment_receipt", + "receipt_reference": ADJUSTMENT_RECEIPT_REFERENCE, + "receipt_digest": ADJUSTMENT_RECEIPT_DIGEST, + "evidence_version": 1, + } + ] + + +def test_component_method_or_artifact_mismatch_fails_closed() -> None: + port = _ReadPort( + adjustment=_adjustment_evidence( + method_reference="weight_method:eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee" + ) + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="adjustment semantics"): + _corroborate(read_port=port) + + +def test_component_not_released_by_final_construction_fails_closed() -> None: + port = _ReadPort( + adjustment=_adjustment_evidence( + released_at=CONSTRUCTED_AT + timedelta(microseconds=1) + ) + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="construction"): + _corroborate(read_port=port) + + +def test_missing_exact_component_receipt_fails_closed() -> None: + port = _ReadPort() + port.adjustment = None + with pytest.raises(FinalWeightComponentEvidenceNotFound): + _corroborate(read_port=port) + + +def test_binding_cannot_omit_a_specialized_adjustment() -> None: + binding = _binding(adjustment_bindings=()) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="specialized"): + _corroborate(read_port=_ReadPort(), binding=binding) + + +def test_binding_cannot_be_released_before_the_final_weight_authority() -> None: + binding_release = FINAL_RELEASED_AT - timedelta(microseconds=1) + binding = _binding( + owner_contract_released_at=binding_release - timedelta(minutes=1), + released_at=binding_release, + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="binding.*final"): + _corroborate(read_port=_ReadPort(), binding=binding) + + +def test_base_component_from_another_study_fails_closed() -> None: + port = _ReadPort(base=_base_evidence(validity_study_id=OTHER_STUDY)) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="tenant or validity study"): + _corroborate(read_port=port) + + +def test_adjustment_component_from_another_tenant_fails_closed() -> None: + port = _ReadPort(adjustment=_adjustment_evidence(tenant_record_id=OTHER_TENANT)) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="tenant or validity study"): + _corroborate(read_port=port) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_edges.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_edges.py new file mode 100644 index 000000000..66f3b5eaa --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_edges.py @@ -0,0 +1,308 @@ +"""Hostile edge coverage for final-weight component evidence resolution.""" + +from __future__ import annotations + +from datetime import timedelta +from types import SimpleNamespace + +import pytest + +import orgmetra_workforce_validation_api.final_weight_component_evidence_resolution as resolution_module +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalAnalysisWeightAuthorityRecord, +) +from orgmetra_workforce_validation_api.final_weight_component_binding_authority import ( + FinalWeightAdjustmentEvidenceBinding, + FinalWeightComponentBindingAuthorityRecord, +) +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + AdjustmentComponentEvidence, + BaseWeightComponentEvidence, + FinalWeightComponentEvidenceIntegrityError, + FinalWeightComponentEvidenceNotFound, + corroborate_final_weight_component_evidence, +) +from test_final_weight_component_evidence_resolution import ( + ADJUSTMENT_RECEIPT_REFERENCE, + BASE_ARTIFACT_DIGEST, + BINDING_RELEASED_AT, + CONSTRUCTED_AT, + FINAL_RELEASED_AT, + OTHER_TENANT, + TENANT, + USED_AT, + _ReadPort, + _adjustment_evidence, + _base_evidence, + _binding, + _corroborate, + _final_weight, + _policy, + _principal, +) + + +def _call_without_owner_rebinding(port: _ReadPort) -> object: + """Invoke corroboration while preserving independently configured owner results.""" + return corroborate_final_weight_component_evidence( + principal=_principal(), + final_weight=_final_weight(), + binding=_binding(), + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=port, + ) + + +def test_component_value_objects_reject_invalid_versions_transforms_and_cutovers() -> None: + """Keep component schema, transform, and half-open chronology fail-closed.""" + with pytest.raises(ValueError, match="evidence_version"): + _base_evidence(evidence_version=2) + with pytest.raises(ValueError, match="superseded_at"): + _base_evidence(superseded_at=CONSTRUCTED_AT - timedelta(minutes=15)) + with pytest.raises(ValueError, match="governed specialized"): + _adjustment_evidence(evidence_kind="unknown_receipt") + with pytest.raises(ValueError, match="evidence_version"): + _adjustment_evidence(evidence_version=2) + with pytest.raises(ValueError, match="transformed"): + _adjustment_evidence(output_weight_artifact_digest=BASE_ARTIFACT_DIGEST) + with pytest.raises(ValueError, match="superseded_at"): + _adjustment_evidence(superseded_at=CONSTRUCTED_AT - timedelta(minutes=1)) + + +def test_canonicalizers_reject_wrong_malformed_and_hidden_record_shapes() -> None: + """Require exact reconstructible runtime types for every cross-owner record.""" + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="base-weight"): + resolution_module._canonical_base_evidence(object()) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="final_weight must"): + resolution_module._canonical_final_weight(object()) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="binding must"): + resolution_module._canonical_binding(object()) + + final_weight = _final_weight() + malformed_final = tuple.__new__( + FinalAnalysisWeightAuthorityRecord, + tuple(final_weight)[:-1], + ) + hidden_final = tuple.__new__( + FinalAnalysisWeightAuthorityRecord, + tuple(final_weight) + ("hidden-final-coordinate",), + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="malformed"): + resolution_module._canonical_final_weight(malformed_final) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="hidden"): + resolution_module._canonical_final_weight(hidden_final) + + binding = _binding() + malformed_binding = tuple.__new__( + FinalWeightComponentBindingAuthorityRecord, + tuple(binding)[:-1], + ) + hidden_binding = tuple.__new__( + FinalWeightComponentBindingAuthorityRecord, + tuple(binding) + ("hidden-binding-coordinate",), + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="malformed"): + resolution_module._canonical_binding(malformed_binding) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="hidden"): + resolution_module._canonical_binding(hidden_binding) + + +def _unused_read(*_args: object, **_kwargs: object) -> None: + """Fail if an incomplete port reaches any owner read.""" + raise AssertionError("incomplete port must fail before owner access") + + +@pytest.mark.parametrize( + "missing_method", + [ + "read_final_analysis_weight_authority", + "read_final_weight_component_binding_authority", + "read_base_weight_component_evidence", + "read_adjustment_component_evidence", + ], +) +def test_every_owner_read_capability_is_required_before_resolution( + missing_method: str, +) -> None: + """Reject a port missing any one of the four static owner capabilities.""" + methods = { + "read_final_analysis_weight_authority": _unused_read, + "read_final_weight_component_binding_authority": _unused_read, + "read_base_weight_component_evidence": _unused_read, + "read_adjustment_component_evidence": _unused_read, + } + del methods[missing_method] + incomplete_port = type("IncompleteComponentReadPort", (), methods)() + + with pytest.raises(TypeError, match=missing_method): + _corroborate(read_port=incomplete_port) + + +def test_final_binding_scope_chronology_and_currentness_fail_closed() -> None: + """Reject cross-receipt, unreleased, or superseded final/binding evidence.""" + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="different"): + _corroborate( + read_port=_ReadPort(), + binding=_binding(analysis_weight_receipt_digest="9" * 64), + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="released before use"): + _corroborate( + read_port=_ReadPort(), + used_at=FINAL_RELEASED_AT - timedelta(microseconds=1), + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="final-weight evidence"): + _corroborate( + read_port=_ReadPort(), + final_weight=_final_weight(superseded_at=USED_AT), + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="binding is not current"): + _corroborate( + read_port=_ReadPort(), + binding=_binding(superseded_at=USED_AT), + ) + + +def test_owner_final_binding_and_base_failures_stop_resolution() -> None: + """Require owner-confirmed final, binding, and base evidence before resolution.""" + port = _ReadPort() + port.final_owner = _final_weight(analytic_case_count=11) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="final-weight owner"): + _call_without_owner_rebinding(port) + + port = _ReadPort() + port.binding_owner = None + with pytest.raises(FinalWeightComponentEvidenceNotFound, match="binding"): + _call_without_owner_rebinding(port) + + port = _ReadPort() + port.base = None + with pytest.raises(FinalWeightComponentEvidenceNotFound): + _call_without_owner_rebinding(port) + + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="base-weight component"): + _corroborate( + read_port=_ReadPort(base=_base_evidence(method_version=2)), + ) + + +def test_component_superseded_by_construction_fails_closed() -> None: + """Reject a component whose authority ended at final-weight construction.""" + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="construction"): + _corroborate( + read_port=_ReadPort( + base=_base_evidence(superseded_at=CONSTRUCTED_AT), + ) + ) + + +def test_adjustment_locator_and_component_identity_must_match() -> None: + """Bind each specialized coordinate to its exact locator and owner projection.""" + mismatched_binding = _binding( + adjustment_bindings=( + FinalWeightAdjustmentEvidenceBinding( + sequence_number=1, + evidence_kind="trimming_bounding_adjustment_receipt", + evidence_receipt_reference=ADJUSTMENT_RECEIPT_REFERENCE.replace( + "nonresponse_adjustment_receipt", + "trimming_bounding_adjustment_receipt", + ), + evidence_version=1, + evidence_receipt_digest="4" * 64, + ), + ) + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="digest or evidence kind"): + _corroborate(read_port=_ReadPort(), binding=mismatched_binding) + + port = _ReadPort( + adjustment=_adjustment_evidence( + receipt_reference=( + "nonresponse_adjustment_receipt:eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee" + ) + ) + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="exact binding locator"): + _corroborate(read_port=port) + + +def test_adjustment_and_base_wrong_runtime_types_fail_closed() -> None: + """Reject non-canonical component objects before reading their structure.""" + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="specialized"): + resolution_module._canonical_adjustment_evidence(object()) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="base-weight"): + resolution_module._canonical_base_evidence(object()) + + +def test_owner_provenance_rejects_cross_tenant_component() -> None: + """Retain tenant provenance before any component semantics are trusted.""" + port = _ReadPort(base=_base_evidence(tenant_record_id=OTHER_TENANT)) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="tenant"): + _corroborate(read_port=port) + + +def _authority_view(record: object, **field_overrides: object) -> SimpleNamespace: + """Expose a detached authority view for post-canonicalization defense tests.""" + fields = dict(record.fields) + fields.update(field_overrides) + return SimpleNamespace( + tenant_record_id=record.tenant_record_id, + validity_study_id=record.validity_study_id, + owner_contract_released_at=record.owner_contract_released_at, + released_at=record.released_at, + superseded_at=record.superseded_at, + fields=fields, + ) + + +@pytest.mark.parametrize( + ("target", "message"), + [ + ("final", "final-weight adjustments"), + ("binding", "component adjustment bindings"), + ], +) +def test_post_canonicalization_collections_remain_immutable( + monkeypatch: pytest.MonkeyPatch, + target: str, + message: str, +) -> None: + """Retain fail-closed collection checks even if a canonicalizer regresses.""" + final_weight = _final_weight() + binding = _binding() + if target == "final": + final_view = _authority_view( + final_weight, + adjustments=list(dict(final_weight.fields)["adjustments"]), + ) + monkeypatch.setattr(resolution_module, "_canonical_final_weight", lambda _value: final_view) + else: + binding_view = _authority_view( + binding, + adjustment_bindings=list(dict(binding.fields)["adjustment_bindings"]), + ) + monkeypatch.setattr(resolution_module, "_canonical_binding", lambda _value: binding_view) + + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match=message): + _corroborate( + read_port=_ReadPort(), + final_weight=final_weight, + binding=binding, + ) + + +def test_post_canonicalization_adjustment_coordinate_type_remains_exact( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Reject a structurally similar adjustment if canonical reconstruction regresses.""" + final_weight = _final_weight() + forged_adjustment = SimpleNamespace( + sequence_number=1, + evidence_kind="nonresponse_adjustment_receipt", + ) + final_view = _authority_view(final_weight, adjustments=(forged_adjustment,)) + monkeypatch.setattr(resolution_module, "_canonical_final_weight", lambda _value: final_view) + + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="coordinates"): + _corroborate(read_port=_ReadPort(), final_weight=final_weight) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_issuance.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_issuance.py new file mode 100644 index 000000000..44deaa276 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_issuance.py @@ -0,0 +1,33 @@ +"""Issuance integrity for corroborated final-weight component evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + BaseWeightComponentEvidence, + FinalWeightComponentEvidenceResolution, +) + + +def test_corroborated_resolution_cannot_be_publicly_forged() -> None: + """Require the proof-bearing resolution to be issued only by corroboration.""" + base = BaseWeightComponentEvidence( + tenant_record_id=UUID("10000000-0000-7000-8000-000000000001"), + validity_study_id=UUID("00000000-0000-7000-8000-0000000000f1"), + receipt_reference=( + "base_weight_evidence_receipt:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + ), + receipt_digest="2" * 64, + evidence_version=1, + method_code="inverse_probability", + method_version=1, + output_weight_artifact_digest="3" * 64, + released_at=datetime(2026, 9, 19, 4, 0, tzinfo=timezone.utc), + ) + + with pytest.raises(TypeError, match="issued only"): + FinalWeightComponentEvidenceResolution(base_weight=base, adjustments=()) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_low_level_issuance.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_low_level_issuance.py new file mode 100644 index 000000000..ef8685500 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_resolution_low_level_issuance.py @@ -0,0 +1,60 @@ +"""Low-level issuance integrity for corroborated final-weight component evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + BaseWeightComponentEvidence, + FinalWeightComponentEvidenceIntegrityError, + FinalWeightComponentEvidenceResolution, +) +from test_final_weight_component_evidence_resolution import _ReadPort, _corroborate + + +def _base_evidence() -> BaseWeightComponentEvidence: + """Return canonical base evidence for hostile result-allocation tests.""" + return BaseWeightComponentEvidence( + tenant_record_id=UUID("10000000-0000-7000-8000-000000000001"), + validity_study_id=UUID("00000000-0000-7000-8000-0000000000f1"), + receipt_reference=( + "base_weight_evidence_receipt:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + ), + receipt_digest="2" * 64, + evidence_version=1, + method_code="inverse_probability", + method_version=1, + output_weight_artifact_digest="3" * 64, + released_at=datetime(2026, 9, 19, 4, 0, tzinfo=timezone.utc), + ) + + +def test_tuple_new_cannot_bypass_corroborated_resolution_issuance() -> None: + """Reject the built-in tuple constructor as an alternate proof issuer.""" + base = _base_evidence() + + with pytest.raises(TypeError): + tuple.__new__(FinalWeightComponentEvidenceResolution, (base, ())) + + +def test_generic_object_allocation_cannot_expose_unsealed_proof() -> None: + """Fail closed if generic allocation produces an exact but unissued result object.""" + forged = object.__new__(FinalWeightComponentEvidenceResolution) + + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="not issued"): + _ = forged.base_weight + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="not issued"): + _ = forged.adjustments + + +def test_canonical_issued_resolution_rejects_public_mutation() -> None: + """Keep corroborated result state immutable after the canonical issuer seals it.""" + resolution = _corroborate(read_port=_ReadPort()) + + with pytest.raises(AttributeError, match="immutable"): + resolution.extra = _base_evidence() # type: ignore[attr-defined] + with pytest.raises(AttributeError, match="immutable"): + del resolution.base_weight diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_scope_binding.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_scope_binding.py new file mode 100644 index 000000000..3f1831343 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_scope_binding.py @@ -0,0 +1,89 @@ +"""Tenant/study provenance for normalized final-weight component evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + AdjustmentComponentEvidence, + BaseWeightComponentEvidence, + FinalWeightComponentEvidenceIntegrityError, + _canonical_adjustment_evidence, + _canonical_base_evidence, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") +RELEASED_AT = datetime(2026, 9, 19, 4, 0, tzinfo=timezone.utc) + + +def _base() -> BaseWeightComponentEvidence: + return BaseWeightComponentEvidence( + tenant_record_id=TENANT, + validity_study_id=STUDY, + receipt_reference="base_weight_evidence_receipt:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", + receipt_digest="2" * 64, + evidence_version=1, + method_code="inverse_probability", + method_version=1, + output_weight_artifact_digest="3" * 64, + released_at=RELEASED_AT, + ) + + +def _adjustment() -> AdjustmentComponentEvidence: + return AdjustmentComponentEvidence( + tenant_record_id=TENANT, + validity_study_id=STUDY, + evidence_kind="nonresponse_adjustment_receipt", + receipt_reference="nonresponse_adjustment_receipt:cccccccc-cccc-4ccc-8ccc-cccccccccccc", + receipt_digest="4" * 64, + evidence_version=1, + method_reference="weight_method:dddddddd-dddd-4ddd-8ddd-dddddddddddd", + method_version=1, + input_weight_artifact_digest="3" * 64, + output_weight_artifact_digest="5" * 64, + configuration_digest="6" * 64, + released_at=RELEASED_AT, + ) + + +def test_base_component_projection_carries_owner_scope() -> None: + base = _base() + assert base.tenant_record_id == TENANT + assert base.validity_study_id == STUDY + assert _canonical_base_evidence(base) == base + + +def test_adjustment_component_projection_carries_owner_scope() -> None: + adjustment = _adjustment() + assert adjustment.tenant_record_id == TENANT + assert adjustment.validity_study_id == STUDY + assert _canonical_adjustment_evidence(adjustment) == adjustment + + +def test_base_component_hidden_scope_structure_fails_closed() -> None: + forged = tuple.__new__(BaseWeightComponentEvidence, tuple(_base()) + ("hidden-scope",)) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="non-canonical"): + _canonical_base_evidence(forged) + + +def test_adjustment_component_hidden_scope_structure_fails_closed() -> None: + forged = tuple.__new__(AdjustmentComponentEvidence, tuple(_adjustment()) + ("hidden-scope",)) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="non-canonical"): + _canonical_adjustment_evidence(forged) + + +def test_base_component_missing_tenant_scope_coordinate_fails_closed() -> None: + forged = tuple.__new__(BaseWeightComponentEvidence, tuple(_base())[1:]) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="malformed"): + _canonical_base_evidence(forged) + + +def test_adjustment_component_missing_tenant_scope_coordinate_fails_closed() -> None: + forged = tuple.__new__(AdjustmentComponentEvidence, tuple(_adjustment())[1:]) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="malformed"): + _canonical_adjustment_evidence(forged) diff --git a/services/workforce-validation-api/tests/test_final_weight_component_evidence_use_currentness.py b/services/workforce-validation-api/tests/test_final_weight_component_evidence_use_currentness.py new file mode 100644 index 000000000..ce9f4ec88 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_component_evidence_use_currentness.py @@ -0,0 +1,36 @@ +"""Governed-use currentness for exact final-weight component evidence.""" + +from __future__ import annotations + +from datetime import timedelta + +import pytest + +from orgmetra_workforce_validation_api.final_weight_component_evidence_resolution import ( + FinalWeightComponentEvidenceIntegrityError, +) +from test_final_weight_component_evidence_resolution import ( + CONSTRUCTED_AT, + _ReadPort, + _adjustment_evidence, + _base_evidence, + _corroborate, +) + + +def test_base_component_superseded_after_construction_before_use_fails_closed() -> None: + port = _ReadPort( + base=_base_evidence(superseded_at=CONSTRUCTED_AT + timedelta(minutes=10)) + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="governed use"): + _corroborate(read_port=port) + + +def test_adjustment_component_superseded_after_construction_before_use_fails_closed() -> None: + port = _ReadPort( + adjustment=_adjustment_evidence( + superseded_at=CONSTRUCTED_AT + timedelta(minutes=10) + ) + ) + with pytest.raises(FinalWeightComponentEvidenceIntegrityError, match="governed use"): + _corroborate(read_port=port) diff --git a/services/workforce-validation-api/tests/test_final_weight_generic_adjustment_resolution.py b/services/workforce-validation-api/tests/test_final_weight_generic_adjustment_resolution.py new file mode 100644 index 000000000..9a9f02b56 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_generic_adjustment_resolution.py @@ -0,0 +1,25 @@ +"""Fail closed when a final-weight adjustment has no released owner resolver contract.""" + +from __future__ import annotations + +import pytest + +from orgmetra_workforce_validation_api.final_weight_authority import ( + FinalWeightAdjustmentCoordinate, +) + + +def test_generic_adjustment_without_governed_receipt_locator_is_rejected() -> None: + """Do not admit a material transform that cannot be re-resolved from owner truth.""" + with pytest.raises(ValueError, match="governed adjustment_code"): + FinalWeightAdjustmentCoordinate( + sequence_number=1, + adjustment_code="custom_transform", + method_reference="weight_method:custom-transform", + method_version=1, + input_weight_artifact_digest="a" * 64, + output_weight_artifact_digest="b" * 64, + configuration_digest="c" * 64, + evidence_receipt_digest="d" * 64, + evidence_kind="custom_transform_receipt", + ) diff --git a/services/workforce-validation-api/tests/test_final_weight_supersession_authority.py b/services/workforce-validation-api/tests/test_final_weight_supersession_authority.py new file mode 100644 index 000000000..a85dcba8a --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_supersession_authority.py @@ -0,0 +1,244 @@ +"""Fail closed when released final analysis-weight evidence has been superseded.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityIntegrityError, + FinalWeightSupersessionAuthorityRecord, + resolve_final_weight_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT_REFERENCE = "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR_REFERENCE = "analysis_weight_receipt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_analysis_weight_receipt_reference", + "successor_correction_sequence", + "successor_analysis_weight_receipt_digest", + "successor_released_at", + } +) + + +class _ReadPort: + """Return one configured owner record through the canonical supersession read shape.""" + + def __init__(self, record: FinalWeightSupersessionAuthorityRecord) -> None: + self.record = record + + def read_final_weight_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + analysis_weight_receipt_reference: str, + analysis_weight_receipt_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> FinalWeightSupersessionAuthorityRecord: + """Return owner evidence; resolver verifies every request coordinate.""" + return self.record + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy() -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-weight-supersession-authority-read-v1", + resource_kind="final_weight_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record( + *, + superseded_at: datetime | None, + successor_reference: str | None, + successor_correction_sequence: int | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> FinalWeightSupersessionAuthorityRecord: + return FinalWeightSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=RECEIPT_REFERENCE, + analysis_weight_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=superseded_at, + successor_analysis_weight_receipt_reference=successor_reference, + successor_correction_sequence=successor_correction_sequence, + successor_analysis_weight_receipt_digest=successor_digest, + successor_released_at=successor_released_at, + ) + + +def _resolve( + record: FinalWeightSupersessionAuthorityRecord, + *, + used_at: datetime = USED_AT, +): + return resolve_final_weight_supersession_authority( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=RECEIPT_REFERENCE, + analysis_weight_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=_ReadPort(record), + ) + + +def test_historical_use_before_supersession_remains_verifiable_without_leaking_successor() -> None: + superseded_at = USED_AT + timedelta(days=1) + view = _resolve( + _record( + superseded_at=superseded_at, + successor_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_digest=SUCCESSOR_DIGEST, + successor_released_at=superseded_at, + ) + ) + + fields = dict(view.fields) + assert fields["analysis_weight_receipt_reference"] == RECEIPT_REFERENCE + assert fields["correction_sequence"] == 2 + assert "superseded_at" not in fields + assert "successor_analysis_weight_receipt_reference" not in fields + assert "successor_released_at" not in fields + + +def test_superseded_final_weight_is_not_authoritative_at_or_after_cutover() -> None: + record = _record( + superseded_at=USED_AT, + successor_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT, + ) + + with pytest.raises(FinalWeightSupersessionAuthorityIntegrityError): + _resolve(record) + + +def test_owner_contract_cannot_be_released_after_final_weight_receipt() -> None: + with pytest.raises(ValueError, match="owner contract must be released no later than final-weight receipt"): + FinalWeightSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=RECEIPT_REFERENCE, + analysis_weight_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=RELEASED_AT + timedelta(seconds=1), + released_at=RELEASED_AT, + ) + + +@pytest.mark.parametrize( + ( + "superseded_at", + "successor_reference", + "successor_correction_sequence", + "successor_digest", + "successor_released_at", + ), + [ + (USED_AT, None, 3, SUCCESSOR_DIGEST, USED_AT), + (None, SUCCESSOR_REFERENCE, 3, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, RECEIPT_REFERENCE, 3, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_REFERENCE, 2, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_REFERENCE, 3, RECEIPT_DIGEST, USED_AT), + ( + RELEASED_AT - timedelta(seconds=1), + SUCCESSOR_REFERENCE, + 3, + SUCCESSOR_DIGEST, + RELEASED_AT - timedelta(seconds=1), + ), + ( + USED_AT, + SUCCESSOR_REFERENCE, + 3, + SUCCESSOR_DIGEST, + USED_AT + timedelta(seconds=1), + ), + ( + USED_AT, + SUCCESSOR_REFERENCE, + 3, + SUCCESSOR_DIGEST, + RELEASED_AT, + ), + ], +) +def test_owner_record_rejects_incomplete_or_non_append_only_supersession_lineage( + superseded_at: datetime | None, + successor_reference: str | None, + successor_correction_sequence: int | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> None: + with pytest.raises(ValueError): + _record( + superseded_at=superseded_at, + successor_reference=successor_reference, + successor_correction_sequence=successor_correction_sequence, + successor_digest=successor_digest, + successor_released_at=successor_released_at, + ) diff --git a/services/workforce-validation-api/tests/test_final_weight_supersession_authority_edges.py b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_edges.py new file mode 100644 index 000000000..23f05ee39 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_edges.py @@ -0,0 +1,282 @@ +"""Hostile edges for append-only final analysis-weight correction authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityIntegrityError, + FinalWeightSupersessionAuthorityNotFound, + FinalWeightSupersessionAuthorityReadPort, + FinalWeightSupersessionAuthorityRecord, + FinalWeightSupersessionAuthorityView, + resolve_final_weight_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT_REFERENCE = "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" +OTHER_RECEIPT_REFERENCE = "analysis_weight_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +SUCCESSOR_REFERENCE = "analysis_weight_receipt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +OTHER_OWNER_CONTRACT_REFERENCE = "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_analysis_weight_receipt_reference", + "successor_correction_sequence", + "successor_analysis_weight_receipt_digest", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_final_weight_supersession_authority(self, **coordinates: object) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(FinalWeightSupersessionAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that static capability validation must reject.""" + + @property + def read_final_weight_supersession_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-weight-supersession-authority-read-v1", + resource_kind="final_weight_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> FinalWeightSupersessionAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": RECEIPT_REFERENCE, + "analysis_weight_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "correction_sequence": 2, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + "successor_analysis_weight_receipt_reference": None, + "successor_correction_sequence": None, + "successor_analysis_weight_receipt_digest": None, + "successor_released_at": None, + } + values.update(overrides) + return FinalWeightSupersessionAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> FinalWeightSupersessionAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": RECEIPT_REFERENCE, + "analysis_weight_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "correction_sequence": 2, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_final_weight_supersession_authority(**values) + + +def test_current_receipt_resolution_uses_owner_release_chronology_without_successor() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, FinalWeightSupersessionAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["analysis_weight_receipt_reference"] == RECEIPT_REFERENCE + assert "owner_contract_released_at" not in port.calls[0] + assert "released_at" not in port.calls[0] + assert "superseded_at" not in port.calls[0] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["owner_contract_released_at"] == OWNER_CONTRACT_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert "superseded_at" not in fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(FinalWeightSupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(FinalWeightSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"analysis_weight_receipt_reference": OTHER_RECEIPT_REFERENCE}, + {"analysis_weight_receipt_digest": "a" * 64}, + {"correction_sequence": 3}, + {"owner_contract_reference": OTHER_OWNER_CONTRACT_REFERENCE}, + {"owner_contract_version": 4}, + {"owner_contract_digest": "b" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(FinalWeightSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_release_chronology_and_use_fail_closed() -> None: + with pytest.raises(FinalWeightSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + record = _record( + superseded_at=USED_AT + timedelta(seconds=1), + successor_analysis_weight_receipt_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_analysis_weight_receipt_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT + timedelta(seconds=1), + ) + view = _resolve(read_port=_ReadPort(record)) + assert dict(view.fields)["analysis_weight_receipt_digest"] == RECEIPT_DIGEST + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("analysis_weight_receipt_reference", "wrong:receipt", ValueError), + ("analysis_weight_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("evidence_version", 2, ValueError), + ("correction_sequence", 0, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "2" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_rejects_non_v1_evidence_and_public_view_construction() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(TypeError, match="issued only by"): + FinalWeightSupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + assert record.successor_fields is None + assert dict(record.fields)["correction_sequence"] == 2 + with pytest.raises(AttributeError): + object.__setattr__(record, "correction_sequence", 3) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) diff --git a/services/workforce-validation-api/tests/test_final_weight_supersession_authority_record_edges.py b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_record_edges.py new file mode 100644 index 000000000..243412603 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_record_edges.py @@ -0,0 +1,65 @@ +"""Record-level hostile edges for final analysis-weight correction authority.""" + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT_REFERENCE = "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR_REFERENCE = "analysis_weight_receipt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +SUPERSEDED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +SUCCESSOR_RELEASED_AT = SUPERSEDED_AT - timedelta(hours=1) + + +def _record(**overrides: object) -> FinalWeightSupersessionAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "analysis_weight_receipt_reference": RECEIPT_REFERENCE, + "analysis_weight_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "correction_sequence": 2, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": SUPERSEDED_AT, + "successor_analysis_weight_receipt_reference": SUCCESSOR_REFERENCE, + "successor_correction_sequence": 3, + "successor_analysis_weight_receipt_digest": SUCCESSOR_DIGEST, + "successor_released_at": SUCCESSOR_RELEASED_AT, + } + values.update(overrides) + return FinalWeightSupersessionAuthorityRecord(**values) + + +@pytest.mark.parametrize( + "overrides", + [ + {"owner_contract_released_at": datetime(2026, 7, 1)}, + {"released_at": datetime(2026, 7, 15)}, + {"superseded_at": datetime(2026, 9, 17)}, + {"successor_analysis_weight_receipt_reference": "wrong:receipt"}, + {"successor_correction_sequence": False}, + {"successor_analysis_weight_receipt_digest": "ABC"}, + {"successor_released_at": datetime(2026, 9, 16, 23, 0)}, + ], +) +def test_record_rejects_malformed_owner_resolved_chronology_or_successor_coordinates( + overrides: dict[str, object], +) -> None: + with pytest.raises(ValueError): + _record(**overrides) diff --git a/services/workforce-validation-api/tests/test_final_weight_supersession_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_structural_integrity.py new file mode 100644 index 000000000..3cf0e3198 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_structural_integrity.py @@ -0,0 +1,143 @@ +"""Structural-integrity regressions for final-weight supersession evidence.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityIntegrityError, + FinalWeightSupersessionAuthorityRecord, + resolve_final_weight_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT = "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR = "analysis_weight_receipt:33333333-3333-4333-8333-333333333333" +OWNER = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_DIGEST = "2" * 64 +OWNER_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +CUTOVER = RELEASED_AT + timedelta(days=30) +READ_FIELDS = frozenset( + { + "analysis_weight_receipt_reference", + "analysis_weight_receipt_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_analysis_weight_receipt_reference", + "successor_correction_sequence", + "successor_analysis_weight_receipt_digest", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured persisted supersession evidence.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_final_weight_supersession_authority(self, **_: object) -> object: + """Return the configured owner evidence.""" + return self.result + + +def _record() -> FinalWeightSupersessionAuthorityRecord: + """Build one canonical final-weight correction edge.""" + return FinalWeightSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=RECEIPT, + analysis_weight_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER, + owner_contract_version=3, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=CUTOVER, + successor_analysis_weight_receipt_reference=SUCCESSOR, + successor_correction_sequence=3, + successor_analysis_weight_receipt_digest=SUCCESSOR_DIGEST, + successor_released_at=CUTOVER, + ) + + +def _resolve(result: object) -> object: + """Resolve the predecessor at a valid pre-cutover use instant.""" + return resolve_final_weight_supersession_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=RECEIPT, + analysis_weight_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER, + owner_contract_version=3, + owner_contract_digest=OWNER_DIGEST, + used_at=RELEASED_AT, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="final-weight-supersession-authority-read-v1", + resource_kind="final_weight_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + FinalWeightSupersessionAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(FinalWeightSupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + FinalWeightSupersessionAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(FinalWeightSupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_duplicate_nested_current_field_cannot_be_normalized_away() -> None: + canonical = _record() + raw = list(canonical) + raw[2] = canonical.fields + (("analysis_weight_receipt_reference", RECEIPT),) + forged = tuple.__new__(FinalWeightSupersessionAuthorityRecord, tuple(raw)) + + with pytest.raises(FinalWeightSupersessionAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_final_weight_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..d2d769a9c --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for final-weight supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityIntegrityError, + FinalWeightSupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_low_level_tuple_construction_cannot_issue_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + FinalWeightSupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("analysis_weight_receipt_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(FinalWeightSupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + FinalWeightSupersessionAuthorityIntegrityError, + match="was not issued by resolve_final_weight_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(FinalWeightSupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + FinalWeightSupersessionAuthorityIntegrityError, + match="was not issued by resolve_final_weight_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(FinalWeightSupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_final_weight_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..b6135a209 --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_supersession_authority_view_seal_capability.py @@ -0,0 +1,58 @@ +"""Hostile sealing-capability regression for final-weight supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.final_weight_supersession_authority as authority_module +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityIntegrityError, + FinalWeightSupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000421") +STUDY = UUID("00000000-0000-0000-0000-000000000422") + + +def _raw_view_with_module_marker() -> FinalWeightSupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(FinalWeightSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__( + view, + "_fields", + (("analysis_weight_receipt_digest", "a" * 64),), + ) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_FINAL_WEIGHT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_final_weight_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_FINAL_WEIGHT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_final_weight_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + FinalWeightSupersessionAuthorityIntegrityError, + match=( + "final-weight supersession view was not issued by " + "resolve_final_weight_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_final_weight_supersession_cutover_alignment.py b/services/workforce-validation-api/tests/test_final_weight_supersession_cutover_alignment.py new file mode 100644 index 000000000..9be62aafa --- /dev/null +++ b/services/workforce-validation-api/tests/test_final_weight_supersession_cutover_alignment.py @@ -0,0 +1,46 @@ +"""Require final-weight successor release to be the exact supersession cutover.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.final_weight_supersession_authority import ( + FinalWeightSupersessionAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT_REFERENCE = "analysis_weight_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR_REFERENCE = "analysis_weight_receipt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +SUPERSEDED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) + + +def test_successor_release_must_equal_final_weight_cutover() -> None: + """Reject an overlap where successor evidence exists before predecessor cutover.""" + with pytest.raises( + ValueError, + match="successor final-weight receipt must be released exactly at supersession", + ): + FinalWeightSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + analysis_weight_receipt_reference=RECEIPT_REFERENCE, + analysis_weight_receipt_digest="1" * 64, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest="2" * 64, + owner_contract_released_at=RELEASED_AT - timedelta(days=1), + released_at=RELEASED_AT, + superseded_at=SUPERSEDED_AT, + successor_analysis_weight_receipt_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_analysis_weight_receipt_digest="3" * 64, + successor_released_at=SUPERSEDED_AT - timedelta(seconds=1), + ) diff --git a/services/workforce-validation-api/tests/test_hash_locked_wheel_record_integration.py b/services/workforce-validation-api/tests/test_hash_locked_wheel_record_integration.py new file mode 100644 index 000000000..ec168496b --- /dev/null +++ b/services/workforce-validation-api/tests/test_hash_locked_wheel_record_integration.py @@ -0,0 +1,115 @@ +"""Require the install lock path itself to reject false wheel installation ledgers.""" + +from __future__ import annotations + +import base64 +import csv +import hashlib +import importlib.util +import io +from pathlib import Path +import zipfile + +import pytest + + +_CONTRACT_PATH = Path(__file__).with_name("test_package_metadata_compatibility.py") +_SPEC = importlib.util.spec_from_file_location( + "_workforce_package_metadata_contract_for_record_integration", + _CONTRACT_PATH, +) +assert _SPEC is not None and _SPEC.loader is not None +_CONTRACT = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(_CONTRACT) + + +def _record_hash(content: bytes) -> str: + """Return one URL-safe unpadded RECORD sha256 digest.""" + digest = hashlib.sha256(content).digest() + encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii") + return f"sha256={encoded}" + + +def _write_wheel( + wheelhouse: Path, + *, + filename: str, + package_root: str, + dist_info_root: str, + metadata: str, + include_py_typed: bool, + corrupt_record: bool = False, +) -> None: + """Write a minimal wheel whose outer bytes can hide a false internal RECORD.""" + members: dict[str, bytes] = { + f"{package_root}/__init__.py": b"", + f"{dist_info_root}/METADATA": metadata.encode("utf-8"), + f"{dist_info_root}/WHEEL": ( + "Wheel-Version: 1.0\n" + "Generator: orgmetra-record-integration-fixture\n" + "Root-Is-Purelib: true\n" + "Tag: py3-none-any\n\n" + ).encode("utf-8"), + } + if include_py_typed: + members[f"{package_root}/py.typed"] = b"" + + record_path = f"{dist_info_root}/RECORD" + output = io.StringIO() + writer = csv.writer(output, lineterminator="\n") + for member_path in sorted(members): + content = members[member_path] + member_hash = _record_hash(content) + if corrupt_record and member_path == f"{package_root}/__init__.py": + member_hash = "sha256=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + writer.writerow((member_path, member_hash, str(len(content)))) + writer.writerow((record_path, "", "")) + members[record_path] = output.getvalue().encode("utf-8") + + with zipfile.ZipFile(wheelhouse / filename, "w") as archive: + for member_path, content in members.items(): + archive.writestr(member_path, content) + + +def test_hash_locked_install_manifest_rejects_false_record(tmp_path: Path) -> None: + """Reject a false RECORD through the exact helper that creates install hashes.""" + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + _write_wheel( + wheelhouse, + filename="orgmetra_keyverse_adapter-0.1.0-py3-none-any.whl", + package_root="orgmetra_keyverse_adapter", + dist_info_root="orgmetra_keyverse_adapter-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-keyverse-adapter\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Provides-Extra: test\n" + "Requires-Dist: pytest>=8.3; extra == 'test'\n" + "Requires-Dist: pytest-cov>=5.0; extra == 'test'\n\n" + ), + include_py_typed=False, + ) + _write_wheel( + wheelhouse, + filename="orgmetra_workforce_validation_api-0.1.0-py3-none-any.whl", + package_root="orgmetra_workforce_validation_api", + dist_info_root="orgmetra_workforce_validation_api-0.1.0.dist-info", + metadata=( + "Metadata-Version: 2.4\n" + "Name: orgmetra-workforce-validation-api\n" + "Version: 0.1.0\n" + "Requires-Python: >=3.12\n" + "Requires-Dist: orgmetra-keyverse-adapter==0.1.0\n\n" + ), + include_py_typed=True, + corrupt_record=True, + ) + + with pytest.raises(AssertionError, match="RECORD"): + _CONTRACT._locked_wheel_requirements( + wheelhouse, + service_version="0.1.0", + keyverse_version="0.1.0", + ) diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority.py new file mode 100644 index 000000000..a549990b7 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority.py @@ -0,0 +1,351 @@ +"""Fail-closed contract for released typed nonresponse-adjustment authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityIntegrityError, + NonresponseAdjustmentAuthorityNotFound, + NonresponseAdjustmentAuthorityReadPort, + NonresponseAdjustmentAuthorityRecord, + NonresponseAdjustmentAuthorityView, + resolve_nonresponse_adjustment_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT_REFERENCE = "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" +DISPOSITION_REFERENCE = "response_disposition_receipt:22222222-2222-4222-8222-222222222222" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:33333333-3333-4333-8333-333333333333" +RECEIPT_DIGEST = "1" * 64 +DISPOSITION_DIGEST = "2" * 64 +POPULATION_DIGEST = "3" * 64 +CONFIGURATION_DIGEST = "4" * 64 +INPUT_WEIGHT_DIGEST = "5" * 64 +OUTPUT_WEIGHT_DIGEST = "6" * 64 +OWNER_CONTRACT_DIGEST = "7" * 64 +DISPOSITION_RELEASED_AT = datetime(2026, 9, 16, 10, 0, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "nonresponse_receipt_reference", + "nonresponse_receipt_digest", + "evidence_version", + "response_disposition_receipt_reference", + "response_disposition_receipt_version", + "response_disposition_receipt_digest", + "response_disposition_receipt_released_at", + "adjustment_population_digest", + "method_reference", + "method_version", + "configuration_digest", + "ineligible_treatment_code", + "unknown_treatment_code", + "unavailable_treatment_code", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured nonresponse authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_nonresponse_adjustment_authority(self, **coordinates: object) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(NonresponseAdjustmentAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must be rejected without executing it.""" + + @property + def read_nonresponse_adjustment_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="nonresponse-adjustment-authority-read-v1", + resource_kind="nonresponse_adjustment_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> NonresponseAdjustmentAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "nonresponse_receipt_reference": RECEIPT_REFERENCE, + "nonresponse_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "response_disposition_receipt_reference": DISPOSITION_REFERENCE, + "response_disposition_receipt_version": 4, + "response_disposition_receipt_digest": DISPOSITION_DIGEST, + "response_disposition_receipt_released_at": DISPOSITION_RELEASED_AT, + "adjustment_population_digest": POPULATION_DIGEST, + "method_reference": "weight_method:response_propensity_cells", + "method_version": 3, + "configuration_digest": CONFIGURATION_DIGEST, + "ineligible_treatment_code": "exclude_ineligible", + "unknown_treatment_code": "retain_unknown_class", + "unavailable_treatment_code": "retain_unavailable_class", + "input_weight_artifact_digest": INPUT_WEIGHT_DIGEST, + "output_weight_artifact_digest": OUTPUT_WEIGHT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 5, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return NonresponseAdjustmentAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> NonresponseAdjustmentAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "nonresponse_receipt_reference": RECEIPT_REFERENCE, + "nonresponse_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "response_disposition_receipt_reference": DISPOSITION_REFERENCE, + "response_disposition_receipt_version": 4, + "response_disposition_receipt_digest": DISPOSITION_DIGEST, + "adjustment_population_digest": POPULATION_DIGEST, + "method_reference": "weight_method:response_propensity_cells", + "method_version": 3, + "configuration_digest": CONFIGURATION_DIGEST, + "ineligible_treatment_code": "exclude_ineligible", + "unknown_treatment_code": "retain_unknown_class", + "unavailable_treatment_code": "retain_unavailable_class", + "input_weight_artifact_digest": INPUT_WEIGHT_DIGEST, + "output_weight_artifact_digest": OUTPUT_WEIGHT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 5, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_nonresponse_adjustment_authority(**values) + + +def test_resolution_binds_versioned_disposition_and_treatment_evidence() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, NonresponseAdjustmentAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["response_disposition_receipt_reference"] == DISPOSITION_REFERENCE + assert port.calls[0]["response_disposition_receipt_version"] == 4 + assert port.calls[0]["response_disposition_receipt_digest"] == DISPOSITION_DIGEST + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert ("method_reference", "weight_method:response_propensity_cells") in view.fields + assert ("ineligible_treatment_code", "exclude_ineligible") in view.fields + assert ("unknown_treatment_code", "retain_unknown_class") in view.fields + assert ("unavailable_treatment_code", "retain_unavailable_class") in view.fields + assert ("response_disposition_receipt_released_at", DISPOSITION_RELEASED_AT) in view.fields + assert ("owner_contract_released_at", OWNER_CONTRACT_RELEASED_AT) in view.fields + assert ("superseded_at", None) in view.fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(NonresponseAdjustmentAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + + with pytest.raises(NonresponseAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"nonresponse_receipt_digest": "a" * 64}, + {"response_disposition_receipt_reference": "response_disposition_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"}, + {"response_disposition_receipt_version": 5}, + {"response_disposition_receipt_digest": "b" * 64}, + {"adjustment_population_digest": "c" * 64}, + {"method_reference": "weight_method:response_propensity_model"}, + {"method_version": 4}, + {"configuration_digest": "d" * 64}, + {"unknown_treatment_code": "exclude_unknown"}, + {"input_weight_artifact_digest": "e" * 64}, + {"output_weight_artifact_digest": "f" * 64}, + {"owner_contract_version": 6}, + {"owner_contract_digest": "0" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(NonresponseAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_owner_resolved_input_release_and_currentness_chronology_fail_closed() -> None: + with pytest.raises(ValueError): + _record(response_disposition_receipt_released_at=CONSTRUCTED_AT + timedelta(seconds=1)) + + with pytest.raises(ValueError): + _record(released_at=CONSTRUCTED_AT - timedelta(seconds=1)) + + with pytest.raises(ValueError, match="superseded_at must be later"): + _record(superseded_at=RELEASED_AT) + + with pytest.raises(ValueError, match="standard-library timezone provider"): + _record(superseded_at=datetime(2026, 9, 16, 14, 0)) + + with pytest.raises(NonresponseAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + cutover = RELEASED_AT + timedelta(hours=1) + historical = _resolve( + read_port=_ReadPort(_record(superseded_at=cutover)), + used_at=cutover - timedelta(seconds=1), + ) + assert ("superseded_at", cutover) in historical.fields + + with pytest.raises( + NonresponseAdjustmentAuthorityIntegrityError, + match="superseded for this scientific-use instant", + ): + _resolve( + read_port=_ReadPort(_record(superseded_at=cutover)), + used_at=cutover, + ) + + +def test_weight_artifact_aliasing_fails_closed() -> None: + with pytest.raises(ValueError): + _record(output_weight_artifact_digest=INPUT_WEIGHT_DIGEST) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("nonresponse_receipt_reference", "wrong:receipt", ValueError), + ("nonresponse_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("response_disposition_receipt_reference", "wrong:receipt", ValueError), + ("response_disposition_receipt_version", 0, ValueError), + ("response_disposition_receipt_digest", "2" * 63, ValueError), + ("adjustment_population_digest", "3" * 65, ValueError), + ("method_reference", "wrong:method", ValueError), + ("method_version", True, ValueError), + ("configuration_digest", "4" * 63, ValueError), + ("ineligible_treatment_code", "Exclude Ineligible", ValueError), + ("unknown_treatment_code", "Unknown Treatment", ValueError), + ("unavailable_treatment_code", "Unavailable Treatment", ValueError), + ("constructed_at", datetime(2026, 9, 16, 12, 0), ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "7" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(record, "method_version", 99) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + NonresponseAdjustmentAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_edges.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_edges.py new file mode 100644 index 000000000..71fedfcd4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_edges.py @@ -0,0 +1,52 @@ +"""Hostile edges for typed nonresponse-adjustment authority.""" + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityRecord, +) + + +def _record(*, evidence_version: object = 1) -> NonresponseAdjustmentAuthorityRecord: + return NonresponseAdjustmentAuthorityRecord( + tenant_record_id=UUID("10000000-0000-7000-8000-000000000001"), + validity_study_id=UUID("00000000-0000-7000-8000-0000000000d1"), + nonresponse_receipt_reference=( + "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + nonresponse_receipt_digest="1" * 64, + evidence_version=evidence_version, + response_disposition_receipt_reference=( + "response_disposition_receipt:22222222-2222-4222-8222-222222222222" + ), + response_disposition_receipt_version=4, + response_disposition_receipt_digest="2" * 64, + response_disposition_receipt_released_at=datetime( + 2026, 9, 16, 10, 0, tzinfo=timezone.utc + ), + adjustment_population_digest="3" * 64, + method_reference="weight_method:response_propensity_cells", + method_version=3, + configuration_digest="4" * 64, + ineligible_treatment_code="exclude_ineligible", + unknown_treatment_code="retain_unknown_class", + unavailable_treatment_code="retain_unavailable_class", + input_weight_artifact_digest="5" * 64, + output_weight_artifact_digest="6" * 64, + constructed_at=datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc), + owner_contract_reference=( + "released_owner_contract:33333333-3333-4333-8333-333333333333" + ), + owner_contract_version=5, + owner_contract_digest="7" * 64, + owner_contract_released_at=datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc), + released_at=datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc), + ) + + +def test_evidence_version_cannot_advance_without_a_contract_revision() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_structural_integrity.py new file mode 100644 index 000000000..5124cf557 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_structural_integrity.py @@ -0,0 +1,33 @@ +"""Structural-integrity regressions for nonresponse-adjustment owner evidence.""" + +from __future__ import annotations + +import pytest + +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityIntegrityError, + NonresponseAdjustmentAuthorityRecord, +) +from test_nonresponse_adjustment_authority import _ReadPort, _record, _resolve + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + NonresponseAdjustmentAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(NonresponseAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + NonresponseAdjustmentAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(NonresponseAdjustmentAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_view_issuance_integrity.py new file mode 100644 index 000000000..6a42f6306 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for nonresponse-adjustment view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityIntegrityError, + NonresponseAdjustmentAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_low_level_tuple_construction_cannot_issue_nonresponse_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + NonresponseAdjustmentAuthorityView, + ( + TENANT.int, + STUDY.int, + (("nonresponse_receipt_digest", "0" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_nonresponse_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(NonresponseAdjustmentAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + NonresponseAdjustmentAuthorityIntegrityError, + match="was not issued by resolve_nonresponse_adjustment_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_nonresponse_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(NonresponseAdjustmentAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + NonresponseAdjustmentAuthorityIntegrityError, + match="was not issued by resolve_nonresponse_adjustment_authority", + ): + _ = forged_view.fields + + +def test_raw_nonresponse_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(NonresponseAdjustmentAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_view_seal_capability.py new file mode 100644 index 000000000..373316d07 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_authority_view_seal_capability.py @@ -0,0 +1,58 @@ +"""Hostile sealing-capability regression for nonresponse-adjustment views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.nonresponse_adjustment_authority as authority_module +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityIntegrityError, + NonresponseAdjustmentAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000425") +STUDY = UUID("00000000-0000-0000-0000-000000000426") + + +def _raw_view_with_module_marker() -> NonresponseAdjustmentAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(NonresponseAdjustmentAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__( + view, + "_fields", + (("nonresponse_receipt_digest", "b" * 64),), + ) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_NONRESPONSE_ADJUSTMENT_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_nonresponse_adjustment_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_NONRESPONSE_ADJUSTMENT_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_nonresponse_adjustment_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + NonresponseAdjustmentAuthorityIntegrityError, + match=( + "nonresponse adjustment view was not issued by " + "resolve_nonresponse_adjustment_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_owner_contract_chronology.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_owner_contract_chronology.py new file mode 100644 index 000000000..de72f9820 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_owner_contract_chronology.py @@ -0,0 +1,84 @@ +"""Fail closed when nonresponse-adjustment owner contracts are retroactive.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityRecord, + resolve_nonresponse_adjustment_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +DISPOSITION_RELEASED_AT = datetime(2026, 9, 16, 10, 0, tzinfo=timezone.utc) +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> NonresponseAdjustmentAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "nonresponse_receipt_reference": ( + "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + "nonresponse_receipt_digest": "1" * 64, + "evidence_version": 1, + "response_disposition_receipt_reference": ( + "response_disposition_receipt:22222222-2222-4222-8222-222222222222" + ), + "response_disposition_receipt_version": 4, + "response_disposition_receipt_digest": "2" * 64, + "response_disposition_receipt_released_at": DISPOSITION_RELEASED_AT, + "adjustment_population_digest": "3" * 64, + "method_reference": "weight_method:response_propensity_cells", + "method_version": 3, + "configuration_digest": "4" * 64, + "ineligible_treatment_code": "exclude_ineligible", + "unknown_treatment_code": "retain_unknown_class", + "unavailable_treatment_code": "retain_unavailable_class", + "input_weight_artifact_digest": "5" * 64, + "output_weight_artifact_digest": "6" * 64, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": ( + "released_owner_contract:33333333-3333-4333-8333-333333333333" + ), + "owner_contract_version": 5, + "owner_contract_digest": "7" * 64, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return NonresponseAdjustmentAuthorityRecord(**values) + + +def test_owner_contract_release_is_owner_resolved_not_a_request_coordinate() -> None: + assert "owner_contract_released_at" not in signature( + resolve_nonresponse_adjustment_authority + ).parameters + + +def test_owner_contract_must_exist_before_nonresponse_receipt_release() -> None: + with pytest.raises(ValueError, match="owner_contract_released_at"): + _record( + owner_contract_released_at=datetime( + 2026, 9, 16, 13, 0, 1, tzinfo=timezone.utc + ) + ) + + +def test_owner_contract_release_requires_timezone_aware_evidence() -> None: + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 16, 12, 30)) + + +def test_contract_released_after_construction_but_before_receipt_release_is_valid() -> None: + record = _record() + assert record.owner_contract_released_at == OWNER_CONTRACT_RELEASED_AT + assert record.released_at == RELEASED_AT diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_edges.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_edges.py new file mode 100644 index 000000000..e1740dec4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_edges.py @@ -0,0 +1,317 @@ +"""Hostile edges for append-only nonresponse-adjustment correction authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.nonresponse_adjustment_supersession_authority import ( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + NonresponseAdjustmentSupersessionAuthorityNotFound, + NonresponseAdjustmentSupersessionAuthorityReadPort, + NonresponseAdjustmentSupersessionAuthorityRecord, + NonresponseAdjustmentSupersessionAuthorityView, + resolve_nonresponse_adjustment_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT = "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" +OTHER_RECEIPT = "nonresponse_adjustment_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +SUCCESSOR = "nonresponse_adjustment_receipt:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +OTHER_OWNER = "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "2" * 64 +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 2, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 1, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "nonresponse_receipt_reference", + "nonresponse_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_nonresponse_receipt_reference", + "successor_nonresponse_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + """Store one owner result and initialize the call ledger.""" + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_nonresponse_adjustment_supersession_authority( + self, **coordinates: object + ) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately omit the required owner-read capability.""" + + +class _ProtocolOnly(NonresponseAdjustmentSupersessionAuthorityReadPort): + """Inherit only the Protocol declaration, not a concrete capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that static capability validation must reject.""" + + @property + def read_nonresponse_adjustment_supersession_authority(self) -> object: + """Trip if dependency validation executes the descriptor.""" + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + """Return one exact workforce-validation principal.""" + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + """Return the purpose-bound policy for nonresponse correction evidence.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="nonresponse-adjustment-supersession-read-v1", + resource_kind="nonresponse_adjustment_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> NonresponseAdjustmentSupersessionAuthorityRecord: + """Build one current nonresponse correction state.""" + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "nonresponse_receipt_reference": RECEIPT, + "nonresponse_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + "successor_nonresponse_receipt_reference": None, + "successor_nonresponse_receipt_digest": None, + "successor_evidence_version": None, + "successor_released_at": None, + } + values.update(overrides) + return NonresponseAdjustmentSupersessionAuthorityRecord(**values) + + +def _resolve( + *, read_port: object, **overrides: object +) -> NonresponseAdjustmentSupersessionAuthorityView: + """Resolve current nonresponse authority with caller-known coordinates only.""" + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "nonresponse_receipt_reference": RECEIPT, + "nonresponse_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_nonresponse_adjustment_supersession_authority(**values) + + +def test_current_receipt_resolution_uses_owner_chronology_without_successor() -> None: + """Resolve a current receipt and keep chronology out of the lookup key.""" + port = _ReadPort(_record()) + view = _resolve(read_port=port) + + assert isinstance(port, NonresponseAdjustmentSupersessionAuthorityReadPort) + assert len(port.calls) == 1 + assert "released_at" not in port.calls[0] + assert "superseded_at" not in port.calls[0] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert dict(view.fields)["released_at"] == RELEASED_AT + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + """Do not consult owner evidence when purpose authorization fails.""" + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + """Reject absent and non-canonical owner evidence.""" + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"nonresponse_receipt_reference": OTHER_RECEIPT}, + {"nonresponse_receipt_digest": "a" * 64}, + {"owner_contract_reference": OTHER_OWNER}, + {"owner_contract_version": 2}, + {"owner_contract_digest": "b" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + """Fail closed if owner evidence differs from any requested coordinate.""" + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_release_chronology_and_historical_use_are_distinct() -> None: + """Reject pre-release use while preserving history before a later cutover.""" + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + record = _record( + superseded_at=CUTOVER, + successor_nonresponse_receipt_reference=SUCCESSOR, + successor_nonresponse_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=CUTOVER, + ) + assert dict(_resolve(read_port=_ReadPort(record)).fields)[ + "nonresponse_receipt_digest" + ] == RECEIPT_DIGEST + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(record), used_at=CUTOVER) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("nonresponse_receipt_reference", "wrong:receipt", ValueError), + ("nonresponse_receipt_digest", "ABC", ValueError), + ("evidence_version", 2, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "2" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + """Validate caller-controlled coordinates before touching the owner port.""" + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_rejects_invalid_chronology_schema_and_public_view() -> None: + """Keep chronology atomic, evidence v1, and minimized views issuer-only.""" + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError, match="later than nonresponse"): + _record( + superseded_at=RELEASED_AT, + successor_nonresponse_receipt_reference=SUCCESSOR, + successor_nonresponse_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=RELEASED_AT, + ) + with pytest.raises(ValueError, match="successor_evidence_version"): + _record( + superseded_at=CUTOVER, + successor_nonresponse_receipt_reference=SUCCESSOR, + successor_nonresponse_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=2, + successor_released_at=CUTOVER, + ) + with pytest.raises(ValueError, match="released after"): + _record( + superseded_at=RELEASED_AT + timedelta(seconds=1), + successor_nonresponse_receipt_reference=SUCCESSOR, + successor_nonresponse_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=RELEASED_AT, + ) + with pytest.raises(TypeError, match="issued only by"): + NonresponseAdjustmentSupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_record_and_view_are_immutable_and_uuid_views_detached() -> None: + """Detach UUIDs and reject mutation of owner records and minimized views.""" + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + assert record.successor_fields is None + with pytest.raises(AttributeError): + object.__setattr__(record, "released_at", USED_AT) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_structural_integrity.py new file mode 100644 index 000000000..0aa6d53de --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_structural_integrity.py @@ -0,0 +1,140 @@ +"""Structural-integrity regressions for nonresponse-adjustment supersession evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.nonresponse_adjustment_supersession_authority import ( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + NonresponseAdjustmentSupersessionAuthorityRecord, + resolve_nonresponse_adjustment_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT = "nonresponse_adjustment_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR = "nonresponse_adjustment_receipt:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "2" * 64 +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 1, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "nonresponse_receipt_reference", + "nonresponse_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_nonresponse_receipt_reference", + "successor_nonresponse_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured persisted supersession evidence.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_nonresponse_adjustment_supersession_authority(self, **_: object) -> object: + """Return the configured owner evidence.""" + return self.result + + +def _record() -> NonresponseAdjustmentSupersessionAuthorityRecord: + """Build one canonical nonresponse correction edge.""" + return NonresponseAdjustmentSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + nonresponse_receipt_reference=RECEIPT, + nonresponse_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=CUTOVER, + successor_nonresponse_receipt_reference=SUCCESSOR, + successor_nonresponse_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=CUTOVER, + ) + + +def _resolve(result: object) -> object: + """Resolve the predecessor at a valid pre-cutover use instant.""" + return resolve_nonresponse_adjustment_supersession_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + nonresponse_receipt_reference=RECEIPT, + nonresponse_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + used_at=RELEASED_AT, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="nonresponse-adjustment-supersession-read-v1", + resource_kind="nonresponse_adjustment_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + NonresponseAdjustmentSupersessionAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + NonresponseAdjustmentSupersessionAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_duplicate_nested_current_field_cannot_be_normalized_away() -> None: + canonical = _record() + raw = list(canonical) + raw[2] = canonical.fields + (("nonresponse_receipt_reference", RECEIPT),) + forged = tuple.__new__(NonresponseAdjustmentSupersessionAuthorityRecord, tuple(raw)) + + with pytest.raises(NonresponseAdjustmentSupersessionAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..c4a6b6bc4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for nonresponse-supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.nonresponse_adjustment_supersession_authority import ( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + NonresponseAdjustmentSupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_low_level_tuple_construction_cannot_issue_nonresponse_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + NonresponseAdjustmentSupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("nonresponse_receipt_digest", "0" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_nonresponse_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(NonresponseAdjustmentSupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + match="was not issued by resolve_nonresponse_adjustment_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_nonresponse_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(NonresponseAdjustmentSupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + match="was not issued by resolve_nonresponse_adjustment_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_nonresponse_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(NonresponseAdjustmentSupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..600bd0002 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_authority_view_seal_capability.py @@ -0,0 +1,58 @@ +"""Hostile sealing-capability regression for nonresponse supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.nonresponse_adjustment_supersession_authority as authority_module +from orgmetra_workforce_validation_api.nonresponse_adjustment_supersession_authority import ( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + NonresponseAdjustmentSupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000423") +STUDY = UUID("00000000-0000-0000-0000-000000000424") + + +def _raw_view_with_module_marker() -> NonresponseAdjustmentSupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(NonresponseAdjustmentSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__( + view, + "_fields", + (("nonresponse_receipt_digest", "b" * 64),), + ) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_NONRESPONSE_ADJUSTMENT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_nonresponse_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_NONRESPONSE_ADJUSTMENT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_nonresponse_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + NonresponseAdjustmentSupersessionAuthorityIntegrityError, + match=( + "nonresponse supersession view was not issued by " + "resolve_nonresponse_adjustment_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_contract.py b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_contract.py new file mode 100644 index 000000000..26424c885 --- /dev/null +++ b/services/workforce-validation-api/tests/test_nonresponse_adjustment_supersession_contract.py @@ -0,0 +1,89 @@ +"""Regression contract for append-only nonresponse-adjustment corrections.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import uuid4 + +import pytest + +from orgmetra_workforce_validation_api.nonresponse_adjustment_authority import ( + NonresponseAdjustmentAuthorityReadPort, + NonresponseAdjustmentAuthorityRecord, +) +from orgmetra_workforce_validation_api.nonresponse_adjustment_supersession_authority import ( + NonresponseAdjustmentSupersessionAuthorityRecord, +) + + +def _released_at() -> datetime: + """Return a stable aware instant for chronology assertions.""" + return datetime(2026, 9, 18, 1, 0, tzinfo=timezone.utc) + + +def _supersession_record(**overrides: object) -> NonresponseAdjustmentSupersessionAuthorityRecord: + """Build one predecessor/successor edge with an atomic correction instant.""" + released_at = _released_at() + values: dict[str, object] = { + "tenant_record_id": uuid4(), + "validity_study_id": uuid4(), + "nonresponse_receipt_reference": "nonresponse_adjustment_receipt:old", + "nonresponse_receipt_digest": "a" * 64, + "evidence_version": 1, + "owner_contract_reference": "released_owner_contract:weighting-v1", + "owner_contract_version": 1, + "owner_contract_digest": "b" * 64, + "owner_contract_released_at": released_at - timedelta(minutes=5), + "released_at": released_at, + "superseded_at": released_at + timedelta(minutes=10), + "successor_nonresponse_receipt_reference": "nonresponse_adjustment_receipt:new", + "successor_nonresponse_receipt_digest": "c" * 64, + "successor_evidence_version": 1, + "successor_released_at": released_at + timedelta(minutes=10), + } + values.update(overrides) + return NonresponseAdjustmentSupersessionAuthorityRecord(**values) + + +def test_ordinary_nonresponse_authority_keeps_cutover_owner_resolved() -> None: + """Ordinary currentness accepts a cutover but never makes it a lookup coordinate.""" + record_parameters = signature(NonresponseAdjustmentAuthorityRecord).parameters + read_parameters = signature( + NonresponseAdjustmentAuthorityReadPort.read_nonresponse_adjustment_authority + ).parameters + assert "superseded_at" in record_parameters + assert "superseded_at" not in read_parameters + assert "owner_contract_released_at" not in read_parameters + assert "released_at" not in read_parameters + + +def test_nonresponse_supersession_requires_atomic_successor_release() -> None: + """A correction edge cannot leave an overlap or gap around the cutover.""" + _supersession_record() + with pytest.raises(ValueError, match="exactly at supersession"): + _supersession_record( + successor_released_at=_released_at() + timedelta(minutes=11) + ) + + +def test_nonresponse_supersession_requires_complete_successor_coordinates() -> None: + """A cutover without a complete released successor is not correction authority.""" + with pytest.raises(ValueError, match="complete released successor"): + _supersession_record(successor_released_at=None) + + +def test_nonresponse_supersession_requires_new_immutable_evidence() -> None: + """A predecessor cannot supersede itself or reuse its evidence digest.""" + with pytest.raises(ValueError, match="new reference"): + _supersession_record( + successor_nonresponse_receipt_reference="nonresponse_adjustment_receipt:old" + ) + with pytest.raises(ValueError, match="new evidence"): + _supersession_record(successor_nonresponse_receipt_digest="a" * 64) + + +def test_nonresponse_supersession_rejects_naive_cutover() -> None: + """Correction chronology must remain timezone-aware owner evidence.""" + with pytest.raises(ValueError, match="standard-library timezone provider"): + _supersession_record(superseded_at=datetime(2026, 9, 18, 1, 10)) diff --git a/services/workforce-validation-api/tests/test_package_metadata_compatibility.py b/services/workforce-validation-api/tests/test_package_metadata_compatibility.py new file mode 100644 index 000000000..b49432414 --- /dev/null +++ b/services/workforce-validation-api/tests/test_package_metadata_compatibility.py @@ -0,0 +1,648 @@ +"""Fail closed when Workforce Validation cannot ship as its declared Python distribution.""" + +from __future__ import annotations + +import base64 +import csv +from email.parser import Parser +import hashlib +from importlib.metadata import version as installed_version +import io +import os +from pathlib import Path, PurePosixPath +import shutil +import subprocess +import sys +import tomllib +import zipfile + +from packaging.markers import Marker +from packaging.requirements import Requirement +from packaging.specifiers import SpecifierSet +from packaging.utils import canonicalize_name, parse_wheel_filename +from packaging.version import Version +import pytest + + +_SERVICE_ROOT = Path(__file__).resolve().parents[1] +_REPOSITORY_ROOT = _SERVICE_ROOT.parents[1] +_KEYVERSE_ROOT = _REPOSITORY_ROOT / "packages" / "keyverse-adapter" +_KEYVERSE_PROJECT = _KEYVERSE_ROOT / "pyproject.toml" +_KEYVERSE_NAME = "orgmetra-keyverse-adapter" +_SERVICE_NAME = "orgmetra-workforce-validation-api" + + +def _toml_document(path: Path) -> dict[str, object]: + """Read static TOML metadata without importing executable package code.""" + with path.open("rb") as stream: + return tomllib.load(stream) + + +def _project_metadata(path: Path) -> dict[str, object]: + """Read one project table and reject malformed package metadata.""" + project = _toml_document(path).get("project") + assert isinstance(project, dict), f"{path} must define a [project] table" + return project + + +def _inclusive_python_floor(raw_specifier: object, *, owner: str) -> Version: + """Resolve one reviewed inclusive Python floor or fail before comparing ranges.""" + assert isinstance(raw_specifier, str), f"{owner} requires-python must be text" + specifiers = tuple(SpecifierSet(raw_specifier)) + assert len(specifiers) == 1 and specifiers[0].operator == ">=", ( + f"{owner} requires-python must remain one explicit inclusive floor; " + "extend this contract before adopting a compound range" + ) + return Version(specifiers[0].version) + + +def _service_build_backend_requirement() -> Requirement: + """Return the service's single exact setuptools build-backend requirement.""" + build_system = _toml_document(_SERVICE_ROOT / "pyproject.toml").get("build-system") + assert isinstance(build_system, dict), "service pyproject must define [build-system]" + raw_requirements = build_system.get("requires") + assert isinstance(raw_requirements, list), "build-system requires must be a list" + requirements = [Requirement(value) for value in raw_requirements] + setuptools_requirements = [ + requirement + for requirement in requirements + if canonicalize_name(requirement.name) == canonicalize_name("setuptools") + ] + assert len(setuptools_requirements) == 1, "service must declare one setuptools backend" + requirement = setuptools_requirements[0] + specifiers = tuple(requirement.specifier) + assert len(specifiers) == 1 and specifiers[0].operator == "==", ( + "service setuptools build backend must remain exactly pinned" + ) + return requirement + + +def _subprocess_environment() -> dict[str, str]: + """Remove checkout and ambient pip discovery inputs before offline acceptance.""" + environment = os.environ.copy() + environment.pop("PYTHONPATH", None) + environment.pop("PYTHONHOME", None) + for name in tuple(environment): + if name.startswith("PIP_"): + environment.pop(name) + environment["PIP_CONFIG_FILE"] = os.devnull + environment["PIP_NO_INDEX"] = "1" + environment["PIP_DISABLE_PIP_VERSION_CHECK"] = "1" + environment["PYTHONWARNINGS"] = "error" + return environment + + +def _venv_python(venv_root: Path) -> Path: + """Return the isolated interpreter path for the current operating system.""" + if os.name == "nt": + return venv_root / "Scripts" / "python.exe" + return venv_root / "bin" / "python" + + +def _sha256(path: Path) -> str: + """Hash one built artifact before it becomes an installation candidate.""" + with path.open("rb") as stream: + return hashlib.file_digest(stream, "sha256").hexdigest() + + +def _record_hash(content: bytes) -> str: + """Return the URL-safe unpadded sha256 representation required by wheel RECORD.""" + digest = hashlib.sha256(content).digest() + encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii") + return f"sha256={encoded}" + + +def _canonical_wheel_member_path(member_path: str, *, wheel_name: str) -> str: + """Return one canonical relative POSIX wheel member path or fail closed.""" + path = PurePosixPath(member_path) + assert path.parts and not path.is_absolute(), ( + f"{wheel_name} contains an absolute or empty wheel member path" + ) + assert ".." not in path.parts and "\\" not in member_path, ( + f"{wheel_name} contains a non-canonical wheel member path" + ) + canonical = path.as_posix() + assert member_path == canonical, ( + f"{wheel_name} contains a non-canonical wheel member path" + ) + return canonical + + +def _validate_wheel_record(wheel_path: Path) -> None: + """Verify canonical member identity and the complete wheel installation RECORD.""" + with zipfile.ZipFile(wheel_path) as archive: + infos = tuple(info for info in archive.infolist() if not info.is_dir()) + archive_paths = [info.filename for info in infos] + assert len(archive_paths) == len(set(archive_paths)), ( + f"{wheel_path.name} contains duplicate archive member paths" + ) + canonical_archive_paths = [ + _canonical_wheel_member_path(path, wheel_name=wheel_path.name) + for path in archive_paths + ] + assert len(canonical_archive_paths) == len(set(canonical_archive_paths)), ( + f"{wheel_path.name} contains normalization-colliding archive member paths" + ) + record_paths = [ + path + for path in canonical_archive_paths + if len(PurePosixPath(path).parts) == 2 + and PurePosixPath(path).parts[0].endswith(".dist-info") + and PurePosixPath(path).name == "RECORD" + ] + assert len(record_paths) == 1, ( + f"{wheel_path.name} must contain exactly one dist-info RECORD" + ) + record_path = record_paths[0] + record_text = archive.read(record_path).decode("utf-8") + rows = tuple(csv.reader(io.StringIO(record_text))) + assert rows, f"{wheel_path.name} RECORD must not be empty" + + recorded: dict[str, tuple[str, str]] = {} + for row in rows: + assert len(row) == 3, f"{wheel_path.name} RECORD rows must have three columns" + member_path, member_hash, member_size = row + canonical_member_path = _canonical_wheel_member_path( + member_path, + wheel_name=wheel_path.name, + ) + assert canonical_member_path not in recorded, ( + f"{wheel_path.name} RECORD contains duplicate path {member_path}" + ) + recorded[canonical_member_path] = (member_hash, member_size) + + assert set(recorded) == set(canonical_archive_paths), ( + f"{wheel_path.name} RECORD must cover every wheel member exactly once" + ) + for info in infos: + canonical_info_path = _canonical_wheel_member_path( + info.filename, + wheel_name=wheel_path.name, + ) + member_hash, member_size = recorded[canonical_info_path] + if canonical_info_path == record_path: + assert member_hash == "" and member_size == "", ( + f"{wheel_path.name} RECORD self-entry must leave hash and size empty" + ) + continue + content = archive.read(info.filename) + assert member_hash == _record_hash(content), ( + f"{wheel_path.name} RECORD sha256 mismatch for {info.filename}" + ) + assert member_size == str(len(content)), ( + f"{wheel_path.name} RECORD size mismatch for {info.filename}" + ) + + +def _validate_wheel_contents( + wheel_path: Path, + *, + package_root: str, + expected_name: str, + expected_version: str, + require_py_typed: bool, +) -> None: + """Reject repository leakage, foreign dist-info identity, or missing package data.""" + with zipfile.ZipFile(wheel_path) as archive: + names = tuple(archive.namelist()) + + assert names, f"{wheel_path.name} must not be empty" + top_levels: set[str] = set() + dist_info_roots: set[str] = set() + for raw_name in names: + parts = PurePosixPath(raw_name).parts + if not parts: + continue + top_levels.add(parts[0]) + if parts[0].endswith(".dist-info"): + dist_info_roots.add(parts[0]) + assert "tests" not in {part.lower() for part in parts}, ( + f"{wheel_path.name} leaked test content: {raw_name}" + ) + assert not raw_name.endswith(".pyc"), ( + f"{wheel_path.name} must not ship bytecode: {raw_name}" + ) + + assert len(dist_info_roots) == 1, ( + f"{wheel_path.name} must contain exactly one .dist-info root" + ) + expected_dist_info_root = ( + f"{canonicalize_name(expected_name).replace('-', '_')}-" + f"{Version(expected_version)}.dist-info" + ) + assert dist_info_roots == {expected_dist_info_root}, ( + f"{wheel_path.name} dist-info identity must be {expected_dist_info_root}, " + f"observed {sorted(dist_info_roots)}" + ) + allowed_top_levels = {package_root, *dist_info_roots} + assert top_levels <= allowed_top_levels, ( + f"{wheel_path.name} contains unexpected top-level content: " + f"{sorted(top_levels - allowed_top_levels)}" + ) + assert package_root in top_levels, ( + f"{wheel_path.name} does not contain expected package root {package_root}" + ) + if require_py_typed: + assert f"{package_root}/py.typed" in names, ( + f"{wheel_path.name} must include declared py.typed package data" + ) + + +def _requirement_identity(requirement: Requirement) -> tuple[str, tuple[str, ...], str, str, str]: + """Normalize one dependency declaration without dropping extras, markers, or direct URLs.""" + return ( + canonicalize_name(requirement.name), + tuple(sorted(canonicalize_name(extra) for extra in requirement.extras)), + str(requirement.specifier), + str(requirement.marker) if requirement.marker is not None else "", + requirement.url or "", + ) + + +def _reviewed_dependency_metadata( + project: dict[str, object], + *, + owner: str, +) -> tuple[tuple[Requirement, ...], tuple[str, ...]]: + """Expand reviewed base and optional dependencies into built METADATA semantics.""" + raw_dependencies = project.get("dependencies", []) + assert isinstance(raw_dependencies, list) and all( + isinstance(value, str) for value in raw_dependencies + ), f"{owner} project dependencies must be a text list" + requirements = [Requirement(value) for value in raw_dependencies] + + raw_optional = project.get("optional-dependencies", {}) + assert isinstance(raw_optional, dict), f"{owner} optional-dependencies must be a table" + extras: list[str] = [] + for raw_extra, raw_requirements in raw_optional.items(): + assert isinstance(raw_extra, str), f"{owner} optional dependency names must be text" + assert isinstance(raw_requirements, list) and all( + isinstance(value, str) for value in raw_requirements + ), f"{owner} optional dependency group {raw_extra} must be a text list" + normalized_extra = canonicalize_name(raw_extra) + extras.append(normalized_extra) + for value in raw_requirements: + requirement = Requirement(value) + extra_marker = f"extra == {normalized_extra!r}" + if requirement.marker is None: + requirement.marker = Marker(extra_marker) + else: + requirement.marker = Marker(f"({requirement.marker}) and {extra_marker}") + requirements.append(requirement) + + assert len(extras) == len(set(extras)), f"{owner} optional dependency extras must be unique" + return tuple(requirements), tuple(sorted(extras)) + + +def _validate_wheel_metadata( + wheel_path: Path, + *, + expected_name: str, + expected_version: str, + expected_requires_python: str, + expected_dependencies: tuple[Requirement, ...], + expected_extras: tuple[str, ...], + required_dependency: tuple[str, str] | None = None, +) -> None: + """Bind built METADATA to reviewed project identity, runtime, dependencies, and extras.""" + with zipfile.ZipFile(wheel_path) as archive: + metadata_paths = [ + name + for name in archive.namelist() + if PurePosixPath(name).name == "METADATA" + and len(PurePosixPath(name).parts) == 2 + and PurePosixPath(name).parts[0].endswith(".dist-info") + ] + assert len(metadata_paths) == 1, ( + f"{wheel_path.name} must contain exactly one dist-info METADATA file" + ) + raw_metadata = archive.read(metadata_paths[0]).decode("utf-8") + + metadata = Parser().parsestr(raw_metadata) + raw_name = metadata.get("Name") + raw_version = metadata.get("Version") + raw_requires_python = metadata.get("Requires-Python") + assert raw_name is not None, f"{wheel_path.name} METADATA must declare Name" + assert raw_version is not None, f"{wheel_path.name} METADATA must declare Version" + assert raw_requires_python is not None, ( + f"{wheel_path.name} METADATA must declare Requires-Python" + ) + assert canonicalize_name(raw_name) == canonicalize_name(expected_name), ( + f"{wheel_path.name} METADATA Name does not match reviewed project identity" + ) + assert Version(raw_version) == Version(expected_version), ( + f"{wheel_path.name} METADATA Version does not match reviewed project version" + ) + assert SpecifierSet(raw_requires_python) == SpecifierSet(expected_requires_python), ( + f"{wheel_path.name} METADATA Requires-Python does not match reviewed project runtime" + ) + + parsed_dependencies = [ + Requirement(value) for value in metadata.get_all("Requires-Dist", failobj=[]) + ] + if required_dependency is not None: + dependency_name, dependency_version = required_dependency + matching_dependencies = [ + requirement + for requirement in parsed_dependencies + if canonicalize_name(requirement.name) == canonicalize_name(dependency_name) + ] + assert len(matching_dependencies) == 1, ( + f"{wheel_path.name} METADATA must preserve the mandatory Keyverse dependency" + ) + requirement = matching_dependencies[0] + assert requirement.specifier == SpecifierSet(f"=={dependency_version}"), ( + f"{wheel_path.name} METADATA must preserve the exact owned Keyverse version" + ) + assert not requirement.extras and requirement.marker is None and requirement.url is None, ( + f"{wheel_path.name} mandatory Keyverse dependency must remain unconditional" + ) + + assert sorted(_requirement_identity(value) for value in parsed_dependencies) == sorted( + _requirement_identity(value) for value in expected_dependencies + ), f"{wheel_path.name} METADATA dependencies do not match reviewed project dependencies" + + built_extras = tuple( + sorted(canonicalize_name(value) for value in metadata.get_all("Provides-Extra", failobj=[])) + ) + assert built_extras == expected_extras, ( + f"{wheel_path.name} METADATA extras do not match reviewed optional dependencies" + ) + + +def _locked_wheel_requirements( + wheelhouse: Path, + *, + service_version: str, + keyverse_version: str, +) -> tuple[str, dict[str, Path]]: + """Validate exact wheel identities and return a hash-locked install manifest.""" + expected_versions = { + canonicalize_name(_KEYVERSE_NAME): Version(keyverse_version), + canonicalize_name(_SERVICE_NAME): Version(service_version), + } + package_roots = { + canonicalize_name(_KEYVERSE_NAME): "orgmetra_keyverse_adapter", + canonicalize_name(_SERVICE_NAME): "orgmetra_workforce_validation_api", + } + source_projects = { + canonicalize_name(_KEYVERSE_NAME): _project_metadata(_KEYVERSE_PROJECT), + canonicalize_name(_SERVICE_NAME): _project_metadata(_SERVICE_ROOT / "pyproject.toml"), + } + wheels_by_name: dict[str, Path] = {} + hashes_by_name: dict[str, str] = {} + + wheel_paths = tuple(sorted(wheelhouse.iterdir())) + assert len(wheel_paths) == len(expected_versions), ( + "distribution acceptance must produce exactly the expected owned wheels" + ) + assert all(path.is_file() and path.suffix == ".whl" for path in wheel_paths), ( + "isolated wheelhouse must contain wheel artifacts only" + ) + for wheel_path in wheel_paths: + parsed_name, parsed_version, build, _tags = parse_wheel_filename(wheel_path.name) + canonical_name = canonicalize_name(parsed_name) + assert build == (), f"{wheel_path.name} must not use an unreviewed build tag" + assert canonical_name in expected_versions, ( + f"unexpected wheel in isolated wheelhouse: {wheel_path.name}" + ) + assert parsed_version == expected_versions[canonical_name], ( + f"{wheel_path.name} version does not match repository metadata" + ) + assert canonical_name not in wheels_by_name, ( + f"duplicate wheel identity for {canonical_name}" + ) + project = source_projects[canonical_name] + requires_python = project.get("requires-python") + assert isinstance(requires_python, str), ( + f"{canonical_name} project requires-python must be text" + ) + reviewed_dependencies, reviewed_extras = _reviewed_dependency_metadata( + project, + owner=canonical_name, + ) + expected_name = ( + _SERVICE_NAME + if canonical_name == canonicalize_name(_SERVICE_NAME) + else _KEYVERSE_NAME + ) + _validate_wheel_record(wheel_path) + _validate_wheel_contents( + wheel_path, + package_root=package_roots[canonical_name], + expected_name=expected_name, + expected_version=str(expected_versions[canonical_name]), + require_py_typed=canonical_name == canonicalize_name(_SERVICE_NAME), + ) + wheels_by_name[canonical_name] = wheel_path + _validate_wheel_metadata( + wheel_path, + expected_name=expected_name, + expected_version=str(expected_versions[canonical_name]), + expected_requires_python=requires_python, + expected_dependencies=reviewed_dependencies, + expected_extras=reviewed_extras, + required_dependency=( + (_KEYVERSE_NAME, keyverse_version) + if canonical_name == canonicalize_name(_SERVICE_NAME) + else None + ), + ) + + assert set(wheels_by_name) == set(expected_versions) + for canonical_name, wheel_path in wheels_by_name.items(): + hashes_by_name[canonical_name] = _sha256(wheel_path) + lock_lines = [ + f"{_KEYVERSE_NAME}=={keyverse_version} --hash=sha256:{hashes_by_name[canonicalize_name(_KEYVERSE_NAME)]}", + f"{_SERVICE_NAME}=={service_version} --hash=sha256:{hashes_by_name[canonicalize_name(_SERVICE_NAME)]}", + ] + return "\n".join(lock_lines) + "\n", wheels_by_name + + +def test_subprocess_environment_blocks_ambient_package_discovery( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Reject inherited pip sources or configuration that could escape the wheelhouse.""" + poisoned = { + "PYTHONPATH": "https://example.invalid/pythonpath", + "PYTHONHOME": "/tmp/example-python-home", + "PIP_FIND_LINKS": "https://example.invalid/find-links", + "PIP_INDEX_URL": "https://example.invalid/simple", + "PIP_EXTRA_INDEX_URL": "https://example.invalid/extra", + "PIP_CONFIG_FILE": "/tmp/example-pip.conf", + "PIP_TRUSTED_HOST": "example.invalid", + } + for name, value in poisoned.items(): + monkeypatch.setenv(name, value) + + environment = _subprocess_environment() + + for name in ( + "PYTHONPATH", + "PYTHONHOME", + "PIP_FIND_LINKS", + "PIP_INDEX_URL", + "PIP_EXTRA_INDEX_URL", + "PIP_TRUSTED_HOST", + ): + assert name not in environment, f"ambient package source leaked through {name}" + assert environment["PIP_NO_INDEX"] == "1" + assert environment["PIP_CONFIG_FILE"] == os.devnull + assert environment["PIP_DISABLE_PIP_VERSION_CHECK"] == "1" + + +def test_service_python_floor_covers_mandatory_keyverse_dependency() -> None: + """Reject a service floor below the exact owned Keyverse dependency floor.""" + service_project = _project_metadata(_SERVICE_ROOT / "pyproject.toml") + keyverse_project = _project_metadata(_KEYVERSE_PROJECT) + + service_dependencies = service_project.get("dependencies") + assert isinstance(service_dependencies, list), "service dependencies must be a list" + parsed_dependencies = [Requirement(value) for value in service_dependencies] + keyverse_requirements = [ + requirement + for requirement in parsed_dependencies + if canonicalize_name(requirement.name) == canonicalize_name(_KEYVERSE_NAME) + ] + assert len(keyverse_requirements) == 1, "service must declare exactly one Keyverse dependency" + + keyverse_version = keyverse_project.get("version") + assert isinstance(keyverse_version, str), "Keyverse project version must be text" + assert keyverse_requirements[0].specifier == SpecifierSet(f"=={keyverse_version}"), ( + "service must consume the exact in-repository Keyverse version" + ) + + service_floor = _inclusive_python_floor( + service_project.get("requires-python"), owner="workforce-validation-api" + ) + keyverse_floor = _inclusive_python_floor( + keyverse_project.get("requires-python"), owner="keyverse-adapter" + ) + assert service_floor >= keyverse_floor, ( + "workforce-validation-api advertises Python versions where its mandatory " + f"Keyverse dependency cannot install: service floor {service_floor}, " + f"Keyverse floor {keyverse_floor}" + ) + + +def test_built_distribution_closure_installs_without_checkout_imports(tmp_path: Path) -> None: + """Hash-bind local wheels and prove the exact dependency closure installs offline.""" + backend_requirement = _service_build_backend_requirement() + assert Version(installed_version("setuptools")) in backend_requirement.specifier, ( + "canonical test/build toolchain must install the service's exact setuptools backend " + f"before distribution acceptance; required {backend_requirement.specifier}, " + f"observed {installed_version('setuptools')}" + ) + + service_project = _project_metadata(_SERVICE_ROOT / "pyproject.toml") + service_version = service_project.get("version") + assert isinstance(service_version, str), "service project version must be text" + keyverse_project = _project_metadata(_KEYVERSE_PROJECT) + keyverse_version = keyverse_project.get("version") + assert isinstance(keyverse_version, str), "Keyverse project version must be text" + + wheelhouse = tmp_path / "wheelhouse" + wheelhouse.mkdir() + build_sources = tmp_path / "build-sources" + build_sources.mkdir() + environment = _subprocess_environment() + for source_root in (_KEYVERSE_ROOT, _SERVICE_ROOT): + build_root = build_sources / source_root.name + shutil.copytree( + source_root, + build_root, + ignore=shutil.ignore_patterns( + "__pycache__", + ".pytest_cache", + ".coverage", + "build", + "dist", + "*.egg-info", + "*.pyc", + ), + ) + subprocess.run( + [ + sys.executable, + "-m", + "pip", + "wheel", + "--no-index", + "--no-cache-dir", + "--no-deps", + "--no-build-isolation", + "--wheel-dir", + str(wheelhouse), + str(build_root), + ], + cwd=tmp_path, + env=environment, + check=True, + ) + + locked_requirements, wheels_by_name = _locked_wheel_requirements( + wheelhouse, + service_version=service_version, + keyverse_version=keyverse_version, + ) + requirements_path = tmp_path / "built-wheel-requirements.txt" + requirements_path.write_text(locked_requirements, encoding="utf-8") + + install_venv = tmp_path / "install-venv" + subprocess.run( + [sys.executable, "-m", "venv", str(install_venv)], + cwd=tmp_path, + env=environment, + check=True, + ) + isolated_python = _venv_python(install_venv) + subprocess.run( + [ + str(isolated_python), + "-m", + "pip", + "install", + "--require-hashes", + "--no-index", + "--no-cache-dir", + "--only-binary=:all:", + f"--find-links={wheelhouse}", + "--requirement", + str(requirements_path), + ], + cwd=tmp_path, + env=environment, + check=True, + ) + subprocess.run( + [str(isolated_python), "-m", "pip", "check"], + cwd=tmp_path, + env=environment, + check=True, + ) + + assert set(wheels_by_name) == { + canonicalize_name(_KEYVERSE_NAME), + canonicalize_name(_SERVICE_NAME), + } + probe = "\n".join( + [ + "from importlib.metadata import version", + "from pathlib import Path", + "import sys", + "import orgmetra_keyverse_adapter", + "import orgmetra_workforce_validation_api", + f"assert version({_KEYVERSE_NAME!r}) == {keyverse_version!r}", + f"assert version({_SERVICE_NAME!r}) == {service_version!r}", + "prefix = Path(sys.prefix).resolve()", + "for module in (orgmetra_keyverse_adapter, orgmetra_workforce_validation_api):", + " module_path = Path(module.__file__).resolve()", + " assert prefix in module_path.parents, (prefix, module_path)", + ] + ) + subprocess.run( + [str(isolated_python), "-c", probe], + cwd=tmp_path, + env=environment, + check=True, + ) diff --git a/services/workforce-validation-api/tests/test_persistence_layout.py b/services/workforce-validation-api/tests/test_persistence_layout.py new file mode 100644 index 000000000..cf77b6bdc --- /dev/null +++ b/services/workforce-validation-api/tests/test_persistence_layout.py @@ -0,0 +1,41 @@ +"""Architecture contract for workforce-validation-owned PostgreSQL persistence.""" + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[3] +MIGRATION = ROOT / "services/workforce-validation-api/database/migrations/0001_owner_schema.sql" +FOUNDATION_WORKFLOW = ROOT / ".github/workflows/foundation-ci.yml" +OWNER_SCHEMA_POSTGRES_CONTRACT = "test_workforce_validation_owner_schema_postgres.sh" + + +def test_owner_schema_migration_establishes_deny_default_role_boundary() -> None: + """Require a service-owned schema and least-privilege database role before adapters.""" + sql = MIGRATION.read_text(encoding="utf-8") + + required = ( + "CREATE ROLE workforce_validation_role NOLOGIN", + "CREATE SCHEMA workforce_validation AUTHORIZATION workforce_validation_role", + "REVOKE ALL ON SCHEMA workforce_validation FROM PUBLIC", + ) + for contract in required: + assert contract in sql + + assert "ALTER ROLE workforce_validation_role SET search_path" not in sql + assert "CREATE TABLE" not in sql + assert "public.validity_study" not in sql + assert "GRANT ALL" not in sql + + +def test_owner_migration_history_is_bounded_context_local() -> None: + """Prevent a new global migration number from colliding with other active lanes.""" + relative_path = MIGRATION.relative_to(ROOT).as_posix() + + assert relative_path == "services/workforce-validation-api/database/migrations/0001_owner_schema.sql" + + +def test_owner_schema_postgres_contract_is_admitted_to_foundation() -> None: + """Require the owner-schema bootstrap to execute in the canonical PostgreSQL matrix.""" + workflow = FOUNDATION_WORKFLOW.read_text(encoding="utf-8") + + assert OWNER_SCHEMA_POSTGRES_CONTRACT in workflow diff --git a/services/workforce-validation-api/tests/test_policy_runtime_integrity.py b/services/workforce-validation-api/tests/test_policy_runtime_integrity.py new file mode 100644 index 000000000..c72a6c228 --- /dev/null +++ b/services/workforce-validation-api/tests/test_policy_runtime_integrity.py @@ -0,0 +1,113 @@ +"""Regression for executable policy scalar values at the validation boundary.""" + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.registry import ( + ValidationPrincipal, + ValidityStudyReadPort, + read_validity_study, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") + + +class _ExecutableText(str): + """Trip if authorization compares this caller-defined string subtype.""" + + calls = 0 + __hash__ = str.__hash__ + + def __eq__(self, other: object) -> bool: + """Expose any equality comparison before the boundary rejects the subtype.""" + type(self).calls += 1 + raise AssertionError("caller-defined policy comparison executed") + + def __ne__(self, other: object) -> bool: + """Expose any inequality comparison before the boundary rejects the subtype.""" + type(self).calls += 1 + raise AssertionError("caller-defined policy comparison executed") + + +class _ReadPort: + """Record whether persistence was reached.""" + + def __init__(self) -> None: + self.calls = 0 + + def read_validity_study(self, *, tenant_record_id: UUID, validity_study_id: UUID) -> None: + """Fail the test if a rejected policy reaches persistence.""" + del tenant_record_id, validity_study_id + self.calls += 1 + return None + + +def test_policy_text_subtype_is_rejected_before_comparison_or_persistence() -> None: + _ExecutableText.calls = 0 + port = _ReadPort() + assert isinstance(port, ValidityStudyReadPort) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-read-v1", + resource_kind=_ExecutableText("validity_study_record"), + purpose_code="validation_review", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset({"study_status_code"}), + ) + + with pytest.raises(ValueError, match="policy resource_kind"): + read_validity_study( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=policy, + read_port=port, + ) + + assert _ExecutableText.calls == 0 + assert port.calls == 0 + + +def test_policy_field_subtype_is_rejected_before_comparison_or_persistence() -> None: + _ExecutableText.calls = 0 + port = _ReadPort() + assert isinstance(port, ValidityStudyReadPort) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-read-v1", + resource_kind="validity_study_record", + purpose_code="validation_review", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset({_ExecutableText("study_status_code")}), + ) + + with pytest.raises(ValueError, match="policy permitted_fields"): + read_validity_study( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=policy, + read_port=port, + ) + + assert _ExecutableText.calls == 0 + assert port.calls == 0 diff --git a/services/workforce-validation-api/tests/test_principal_storage_integrity.py b/services/workforce-validation-api/tests/test_principal_storage_integrity.py new file mode 100644 index 000000000..a0b299859 --- /dev/null +++ b/services/workforce-validation-api/tests/test_principal_storage_integrity.py @@ -0,0 +1,83 @@ +"""Regression contract for canonical validation-principal storage before authorization.""" + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.registry import ( + ValidationPrincipal, + ValidityStudyRecord, + read_validity_study, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") + + +class _ExecutableUUID(UUID): + """Expose executable behavior if a UUID subtype reaches downstream validation.""" + + def __getattribute__(self, name: str) -> object: + if name == "int": + raise AttributeError("UUID subtype behavior executed") + return super().__getattribute__(name) + + +class _ReadPort: + """Capture repository use; this regression must fail before persistence.""" + + def __init__(self) -> None: + self.calls: list[tuple[UUID, UUID]] = [] + + def read_validity_study( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + ) -> ValidityStudyRecord | None: + """Record an unexpected persistence call.""" + self.calls.append((tenant_record_id, validity_study_id)) + return None + + +def _policy() -> PurposeBoundAccessPolicy: + """Return the canonical purpose-bound policy used by the read boundary.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-read-v1", + resource_kind="validity_study_record", + purpose_code="validation_review", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset({"study_status_code"}), + ) + + +def test_low_level_exact_principal_is_revalidated_before_keyverse_evaluation() -> None: + """Reject constructor-bypassed identity evidence before subtype behavior can execute.""" + forged_tenant = _ExecutableUUID(str(TENANT)) + principal = tuple.__new__( + ValidationPrincipal, + ( + forged_tenant, + "person:analyst-1", + frozenset({"orgmetra.workforce_validation.read"}), + ), + ) + port = _ReadPort() + + with pytest.raises(ValueError, match="tenant_record_id must be an exact operational UUID"): + read_validity_study( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(), + read_port=port, + ) + + assert port.calls == [] diff --git a/services/workforce-validation-api/tests/test_production_docstring_contract.py b/services/workforce-validation-api/tests/test_production_docstring_contract.py new file mode 100644 index 000000000..201de93d8 --- /dev/null +++ b/services/workforce-validation-api/tests/test_production_docstring_contract.py @@ -0,0 +1,76 @@ +"""Enforce a measurable documentation floor across owned Workforce Validation code. + +CodeRabbit's PR-wide percentage is useful review signal but also counts test +helpers. This repository-native contract instead scans the production package +recursively and makes undocumented production definitions a hard CI failure. +The mechanical floor rejects blank and placeholder-sized docstrings; semantic +quality remains subject to code review so the metric cannot reward filler. +""" + +from __future__ import annotations + +import ast +from pathlib import Path +import re + + +_PACKAGE_ROOT = ( + Path(__file__).resolve().parents[1] + / "src" + / "orgmetra_workforce_validation_api" +) +_MIN_DOCSTRING_CHARACTERS = 12 +_MIN_DOCSTRING_WORDS = 2 +_WORD_PATTERN = re.compile(r"[A-Za-z][A-Za-z0-9_-]*") +_PLACEHOLDER_DOCSTRINGS = frozenset({"todo", "tbd", "fixme", "pass", "placeholder"}) + + +def _is_substantive_docstring(node: ast.AST) -> bool: + """Reject absent, blank, one-token, and placeholder-sized documentation.""" + docstring = ast.get_docstring(node, clean=False) + if docstring is None: + return False + normalized = " ".join(docstring.split()) + if not normalized or normalized.casefold() in _PLACEHOLDER_DOCSTRINGS: + return False + return ( + len(normalized) >= _MIN_DOCSTRING_CHARACTERS + and len(_WORD_PATTERN.findall(normalized)) >= _MIN_DOCSTRING_WORDS + ) + + +def _symbol_label(path: Path, node: ast.AST) -> str: + """Return one stable repository-relative location for a documentation failure.""" + relative = path.relative_to(_PACKAGE_ROOT).as_posix() + name = getattr(node, "name", "") + line = getattr(node, "lineno", 1) + return f"{relative}:{line}:{name}" + + +def _collect_missing_docstrings(path: Path) -> list[str]: + """Collect every production definition below the minimum documentation floor.""" + module = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + missing: list[str] = [] + + if not _is_substantive_docstring(module): + missing.append(_symbol_label(path, module)) + + for node in ast.walk(module): + if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)) and not ( + _is_substantive_docstring(node) + ): + missing.append(_symbol_label(path, node)) + return missing + + +def test_owned_production_definitions_have_substantive_docstrings() -> None: + """Require the documentation floor for all recursively discovered production Python.""" + source_files = sorted(_PACKAGE_ROOT.rglob("*.py")) + assert source_files, "workforce-validation production package must contain Python sources" + + missing = [ + symbol + for source_file in source_files + for symbol in _collect_missing_docstrings(source_file) + ] + assert not missing, "insufficient production docstrings:\n" + "\n".join(missing) diff --git a/services/workforce-validation-api/tests/test_public_authorized_view_census.py b/services/workforce-validation-api/tests/test_public_authorized_view_census.py new file mode 100644 index 000000000..279cb5835 --- /dev/null +++ b/services/workforce-validation-api/tests/test_public_authorized_view_census.py @@ -0,0 +1,61 @@ +"""Regression contract for the complete public authorized-view export census.""" + +from __future__ import annotations + +import orgmetra_workforce_validation_api as api + + +_EXPECTED_AUTHORIZED_VIEW_NAMES = frozenset( + { + "BaseWeightAuthorityView", + "BaseWeightSupersessionAuthorityView", + "CalibrationAdjustmentAuthorityView", + "CalibrationAdjustmentSupersessionAuthorityView", + "CalibrationAuxiliaryAuthorityView", + "CalibrationBenchmarkAuthorityView", + "CalibrationSupportAuthorityView", + "FinalAnalysisWeightAuthorityView", + "FinalWeightComponentBindingAuthorityView", + "FinalWeightSupersessionAuthorityView", + "NonresponseAdjustmentAuthorityView", + "NonresponseAdjustmentSupersessionAuthorityView", + "TrimmingBoundingAuthorityView", + "TrimmingBoundingSupersessionAuthorityView", + "ValidationResultAuthorityView", + "ValidationResultNonVerifiabilitySupersessionAuthorityView", + "ValidationResultNonVerifiabilitySupersessionV2AuthorityView", + "ValidationResultNonVerifiabilityView", + "ValidationResultSupersessionAuthorityView", + "ValidityStudyView", + "WeightEligibilityAuthorityView", + "WeightEligibilitySupersessionAuthorityView", + "WeightVarianceAuthorityView", + "WeightVarianceSupersessionAuthorityView", + } +) + + +def test_public_authorized_view_export_census_is_explicit_and_non_tuple() -> None: + """Keep every public authorized projection inside the sealed non-tuple contract.""" + observed = frozenset(name for name in api.__all__ if name.endswith("View")) + + assert observed == _EXPECTED_AUTHORIZED_VIEW_NAMES + for name in sorted(observed): + view_type = getattr(api, name) + assert type(view_type) is type + assert not issubclass(view_type, tuple) + + +def test_public_authorized_views_retain_local_sealing_and_mutation_guards() -> None: + """Require each public authorized projection to keep its local issuance boundary.""" + for name in sorted(_EXPECTED_AUTHORIZED_VIEW_NAMES): + view_type = getattr(api, name) + slots = view_type.__dict__.get("__slots__") + + assert type(slots) is tuple + assert "_issuance_marker" in slots + assert "__dict__" not in slots + assert "__new__" in view_type.__dict__ + assert "__setattr__" in view_type.__dict__ + assert "__delattr__" in view_type.__dict__ + assert "_require_issued" in view_type.__dict__ diff --git a/services/workforce-validation-api/tests/test_read_port_dependency_integrity.py b/services/workforce-validation-api/tests/test_read_port_dependency_integrity.py new file mode 100644 index 000000000..e49cefc92 --- /dev/null +++ b/services/workforce-validation-api/tests/test_read_port_dependency_integrity.py @@ -0,0 +1,141 @@ +"""Regression contracts for inert repository capability validation before authorization.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.registry import ( + ValidationPrincipal, + ValidityStudyReadPort, + ValidityStudyRecord, + read_validity_study, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") +CRITERION = UUID("00000000-0000-7000-8000-0000000000a1") +RECORDED_FROM = datetime(2026, 11, 3, tzinfo=timezone.utc) + + +class _DescriptorReadPort: + """Expose a non-callable static protocol member whose getter must never execute.""" + + @property + def read_validity_study(self) -> object: + """Trip if dependency validation or later code executes this descriptor.""" + raise AssertionError("repository descriptor executed before rejection") + + +class _DynamicLookupReadPort: + """Expose one safe class method but a different callable through instance lookup.""" + + def __init__(self) -> None: + self.dynamic_lookups = 0 + self.static_calls = 0 + + def __getattribute__(self, name: str) -> object: + """Trip if the authorized path performs a second dynamic capability lookup.""" + if name == "read_validity_study": + dynamic_lookups = object.__getattribute__(self, "dynamic_lookups") + object.__setattr__(self, "dynamic_lookups", dynamic_lookups + 1) + + def switched_capability(*, tenant_record_id: UUID, validity_study_id: UUID) -> object: + del tenant_record_id, validity_study_id + raise AssertionError("dynamic repository capability lookup executed after validation") + + return switched_capability + return object.__getattribute__(self, name) + + def read_validity_study( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + ) -> ValidityStudyRecord: + """Return valid owner evidence when the statically validated method is invoked.""" + self.static_calls += 1 + return ValidityStudyRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + criterion_blueprint_id=CRITERION, + study_status_code="study_draft", + recorded_from=RECORDED_FROM, + recorded_to=None, + ) + + +class _InheritedProtocolReadPort(ValidityStudyReadPort): + """Intentionally inherit the Protocol declaration without implementing persistence.""" + + +def _principal() -> ValidationPrincipal: + """Return one exact authenticated validation principal.""" + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "validation_review") -> PurposeBoundAccessPolicy: + """Return one purpose-bound policy for the focused repository tests.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-read-v1", + resource_kind="validity_study_record", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset({"study_status_code"}), + ) + + +def test_noncallable_repository_capability_fails_before_authorization() -> None: + """Reject an invalid port before a deliberately denying policy can be evaluated.""" + with pytest.raises(TypeError, match="read_port must expose a statically callable read_validity_study"): + read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(purpose_code="audit_review"), + read_port=_DescriptorReadPort(), # type: ignore[arg-type] + ) + + +def test_inherited_protocol_placeholder_fails_before_authorization() -> None: + """Require a concrete repository implementation before Keyverse policy evaluation.""" + with pytest.raises(TypeError, match="read_port must expose a statically callable read_validity_study"): + read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(purpose_code="audit_review"), + read_port=_InheritedProtocolReadPort(), + ) + + +def test_validated_repository_capability_is_the_capability_invoked_after_authorization() -> None: + """Bind the inertly validated class method instead of re-resolving it dynamically.""" + port = _DynamicLookupReadPort() + + view = read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(), + read_port=port, + ) + + assert port.dynamic_lookups == 0 + assert port.static_calls == 1 + assert view.fields == (("study_status_code", "study_draft"),) diff --git a/services/workforce-validation-api/tests/test_registry.py b/services/workforce-validation-api/tests/test_registry.py new file mode 100644 index 000000000..53d5a62f1 --- /dev/null +++ b/services/workforce-validation-api/tests/test_registry.py @@ -0,0 +1,299 @@ +"""Regression contract for the workforce-validation study registry boundary.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID +from zoneinfo import ZoneInfo + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.registry import ( + ValidationPrincipal, + ValidityStudyIntegrityError, + ValidityStudyNotFound, + ValidityStudyReadPort, + ValidityStudyRecord, + read_validity_study, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000c2") +CRITERION = UUID("00000000-0000-7000-8000-0000000000a1") +RECORDED_FROM = datetime(2026, 11, 3, tzinfo=timezone.utc) + + +class _ReadPort: + """Return one configured registry record and capture the authorized target.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[tuple[UUID, UUID]] = [] + + def read_validity_study(self, *, tenant_record_id: UUID, validity_study_id: UUID) -> object: + """Capture the target and return the configured persistence result.""" + self.calls.append((tenant_record_id, validity_study_id)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the runtime repository protocol.""" + + +def _record(*, tenant_record_id: UUID = TENANT, validity_study_id: UUID = STUDY) -> ValidityStudyRecord: + return ValidityStudyRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + criterion_blueprint_id=CRITERION, + study_status_code="study_draft", + recorded_from=RECORDED_FROM, + recorded_to=None, + ) + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, tenant_record_id: UUID = TENANT) -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=tenant_record_id, + policy_version_code="validation-read-v1", + resource_kind="validity_study_record", + purpose_code="validation_review", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset( + { + "criterion_blueprint_id", + "study_status_code", + "recorded_from", + "recorded_to", + } + ), + ) + + +def test_read_returns_only_authorized_requested_fields() -> None: + port = _ReadPort(_record()) + + view = read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code", "criterion_blueprint_id"}), + policy=_policy(), + read_port=port, + ) + + assert isinstance(port, ValidityStudyReadPort) + assert port.calls == [(TENANT, STUDY)] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert view.fields == ( + ("criterion_blueprint_id", CRITERION), + ("study_status_code", "study_draft"), + ) + + +def test_authorization_denial_happens_before_persistence() -> None: + port = _ReadPort(_record()) + + with pytest.raises(AuthorizationDeniedError): + read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"recorded_from"}), + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-read-v1", + resource_kind="validity_study_record", + purpose_code="audit_review", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset({"recorded_from"}), + ), + read_port=port, + ) + + assert port.calls == [] + + +def test_missing_study_is_not_found() -> None: + with pytest.raises(ValidityStudyNotFound): + read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(), + read_port=_ReadPort(None), + ) + + +def test_foreign_or_noncanonical_persistence_result_fails_closed() -> None: + for result in (_record(tenant_record_id=OTHER_TENANT), _record(validity_study_id=OTHER_STUDY), object()): + with pytest.raises(ValidityStudyIntegrityError): + read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(), + read_port=_ReadPort(result), + ) + + +def test_dependency_and_request_types_fail_before_repository_use() -> None: + port = _ReadPort(_record()) + common = dict( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(), + read_port=port, + ) + + for key, value, error in ( + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("purpose_code", "Validation Review", ValueError), + ("purpose_code", 7, ValueError), + ("requested_fields", set({"study_status_code"}), ValueError), + ("requested_fields", frozenset(), ValueError), + ("requested_fields", frozenset({"unknown_field"}), ValueError), + ("requested_fields", frozenset({7}), ValueError), + ): + arguments = dict(common) + arguments[key] = value + with pytest.raises(error): + read_validity_study(**arguments) + + assert port.calls == [] + + +def test_principal_rejects_invalid_identity_and_scope_shapes() -> None: + invalid_values = ( + dict(tenant_record_id=UUID(int=0), actor_reference="person:analyst-1", granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"})), + dict(tenant_record_id=TENANT, actor_reference="not namespaced", granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"})), + dict(tenant_record_id=TENANT, actor_reference=7, granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"})), + dict(tenant_record_id=TENANT, actor_reference="person:analyst-1", granted_scope_codes=frozenset()), + dict(tenant_record_id=TENANT, actor_reference="person:analyst-1", granted_scope_codes=frozenset({"bad-scope"})), + dict(tenant_record_id=TENANT, actor_reference="person:analyst-1", granted_scope_codes=frozenset({7})), + ) + for values in invalid_values: + with pytest.raises(ValueError): + ValidationPrincipal(**values) + + +def test_principal_is_structurally_immutable_after_identity_validation() -> None: + principal = _principal() + + for field_name, replacement in ( + ("tenant_record_id", OTHER_TENANT), + ("actor_reference", "person:attacker-2"), + ("granted_scope_codes", frozenset({"orgmetra.audit.read"})), + ): + with pytest.raises(AttributeError): + object.__setattr__(principal, field_name, replacement) + + assert principal.tenant_record_id == TENANT + assert principal.actor_reference == "person:analyst-1" + assert principal.granted_scope_codes == frozenset({"orgmetra.workforce_validation.read"}) + + +def test_record_rejects_noncanonical_or_invalid_durable_scalars() -> None: + valid = dict( + tenant_record_id=TENANT, + validity_study_id=STUDY, + criterion_blueprint_id=CRITERION, + study_status_code="study_draft", + recorded_from=RECORDED_FROM, + recorded_to=None, + ) + cases = ( + ("tenant_record_id", UUID(int=0)), + ("validity_study_id", "not-a-uuid"), + ("criterion_blueprint_id", UUID(int=(1 << 128) - 1)), + ("study_status_code", "Study Draft"), + ("study_status_code", 7), + ("recorded_from", datetime(2026, 11, 3)), + ("recorded_to", "not-a-datetime"), + ) + for field_name, value in cases: + arguments = dict(valid) + arguments[field_name] = value + with pytest.raises(ValueError): + ValidityStudyRecord(**arguments) + + with pytest.raises(ValueError): + ValidityStudyRecord(**{**valid, "recorded_to": RECORDED_FROM}) + + +def test_valid_record_detaches_supported_timezones_to_utc() -> None: + for provider in (timezone(timedelta(hours=9)), ZoneInfo("Asia/Seoul")): + record = ValidityStudyRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + criterion_blueprint_id=CRITERION, + study_status_code="study_draft", + recorded_from=datetime(2026, 11, 3, 9, tzinfo=provider), + recorded_to=datetime(2026, 11, 4, 9, tzinfo=provider), + ) + + assert type(record.recorded_from) is datetime + assert record.recorded_from.tzinfo is timezone.utc + assert record.recorded_from.hour == 0 + assert record.recorded_to is not None + assert record.recorded_to.tzinfo is timezone.utc + + +def test_record_is_structurally_immutable_against_object_setattr() -> None: + record = _record() + + with pytest.raises(AttributeError): + object.__setattr__(record, "study_status_code", "study_closed") + + assert record.study_status_code == "study_draft" + + +def test_authorized_view_is_structurally_immutable_after_field_minimization() -> None: + view = read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(), + read_port=_ReadPort(_record()), + ) + original_fields = view.fields + + for field_name, replacement in ( + ("tenant_record_id", OTHER_TENANT), + ("validity_study_id", OTHER_STUDY), + ("fields", (("study_status_code", "study_closed"),)), + ): + with pytest.raises(AttributeError): + object.__setattr__(view, field_name, replacement) + + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert view.fields == original_fields diff --git a/services/workforce-validation-api/tests/test_registry_record_structural_integrity.py b/services/workforce-validation-api/tests/test_registry_record_structural_integrity.py new file mode 100644 index 000000000..d7f50e93a --- /dev/null +++ b/services/workforce-validation-api/tests/test_registry_record_structural_integrity.py @@ -0,0 +1,44 @@ +"""Structural-integrity regressions for persisted validity-study records.""" + +from __future__ import annotations + +import pytest + +from orgmetra_workforce_validation_api.registry import ( + ValidityStudyIntegrityError, + ValidityStudyRecord, + read_validity_study, +) +from test_registry import STUDY, TENANT, _ReadPort, _policy, _principal, _record + + +def _resolve(result: object) -> object: + """Resolve one requested field through the canonical registry boundary.""" + return read_validity_study( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + purpose_code="validation_review", + requested_fields=frozenset({"study_status_code"}), + policy=_policy(), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + ValidityStudyRecord, + tuple(canonical) + ("hidden-persistence-coordinate",), + ) + + with pytest.raises(ValidityStudyIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__(ValidityStudyRecord, tuple(canonical)[:-1]) + + with pytest.raises(ValidityStudyIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_result_supersession_authority.py b/services/workforce-validation-api/tests/test_result_supersession_authority.py new file mode 100644 index 000000000..1198b50bc --- /dev/null +++ b/services/workforce-validation-api/tests/test_result_supersession_authority.py @@ -0,0 +1,243 @@ +"""Fail closed when a released validation result has been superseded.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_supersession_authority import ( + ValidationResultSupersessionAuthorityIntegrityError, + ValidationResultSupersessionAuthorityRecord, + resolve_validation_result_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +SUCCESSOR_REFERENCE = "validation_analysis_result:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RESULT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +RELEASED_AT = datetime(2026, 9, 17, 10, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 12, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_result_reference", + "successor_correction_sequence", + "successor_result_digest", + "successor_released_at", + } +) + + +class _ReadPort: + """Return one configured owner record through the result supersession read shape.""" + + def __init__(self, record: ValidationResultSupersessionAuthorityRecord) -> None: + self.record = record + + def read_validation_result_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + evidence_version: int, + correction_sequence: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultSupersessionAuthorityRecord: + """Return owner evidence; resolver verifies every request coordinate.""" + return self.record + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy() -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-supersession-authority-read-v1", + resource_kind="validation_result_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record( + *, + superseded_at: datetime | None, + successor_reference: str | None, + successor_correction_sequence: int | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> ValidationResultSupersessionAuthorityRecord: + return ValidationResultSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=superseded_at, + successor_result_reference=successor_reference, + successor_correction_sequence=successor_correction_sequence, + successor_result_digest=successor_digest, + successor_released_at=successor_released_at, + ) + + +def _resolve( + record: ValidationResultSupersessionAuthorityRecord, + *, + used_at: datetime = USED_AT, +): + return resolve_validation_result_supersession_authority( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=_ReadPort(record), + ) + + +def test_historical_result_use_before_supersession_remains_verifiable_without_leaking_successor() -> None: + view = _resolve( + _record( + superseded_at=USED_AT + timedelta(days=1), + successor_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT + timedelta(days=1), + ) + ) + + fields = dict(view.fields) + assert fields["result_reference"] == RESULT_REFERENCE + assert fields["correction_sequence"] == 2 + assert "superseded_at" not in fields + assert "successor_result_reference" not in fields + assert "successor_released_at" not in fields + + +def test_superseded_result_is_not_authoritative_at_or_after_cutover() -> None: + record = _record( + superseded_at=USED_AT, + successor_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT, + ) + + with pytest.raises(ValidationResultSupersessionAuthorityIntegrityError): + _resolve(record) + + +def test_owner_contract_cannot_be_released_after_result() -> None: + with pytest.raises(ValueError, match="owner contract must be released no later than validation result"): + ValidationResultSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=RELEASED_AT + timedelta(seconds=1), + released_at=RELEASED_AT, + ) + + +@pytest.mark.parametrize( + ( + "superseded_at", + "successor_reference", + "successor_correction_sequence", + "successor_digest", + "successor_released_at", + ), + [ + (USED_AT, None, 3, SUCCESSOR_DIGEST, USED_AT), + (None, SUCCESSOR_REFERENCE, 3, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, RESULT_REFERENCE, 3, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_REFERENCE, 2, SUCCESSOR_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_REFERENCE, 3, RESULT_DIGEST, USED_AT), + ( + RELEASED_AT - timedelta(seconds=1), + SUCCESSOR_REFERENCE, + 3, + SUCCESSOR_DIGEST, + RELEASED_AT - timedelta(seconds=1), + ), + ( + USED_AT, + SUCCESSOR_REFERENCE, + 3, + SUCCESSOR_DIGEST, + USED_AT + timedelta(seconds=1), + ), + ( + USED_AT, + SUCCESSOR_REFERENCE, + 3, + SUCCESSOR_DIGEST, + RELEASED_AT, + ), + ], +) +def test_owner_record_rejects_incomplete_or_non_append_only_result_supersession( + superseded_at: datetime | None, + successor_reference: str | None, + successor_correction_sequence: int | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> None: + with pytest.raises(ValueError): + _record( + superseded_at=superseded_at, + successor_reference=successor_reference, + successor_correction_sequence=successor_correction_sequence, + successor_digest=successor_digest, + successor_released_at=successor_released_at, + ) diff --git a/services/workforce-validation-api/tests/test_result_supersession_authority_edges.py b/services/workforce-validation-api/tests/test_result_supersession_authority_edges.py new file mode 100644 index 000000000..29082cab4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_result_supersession_authority_edges.py @@ -0,0 +1,321 @@ +"""Hostile edges for append-only validation-result correction authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_supersession_authority import ( + ValidationResultSupersessionAuthorityIntegrityError, + ValidationResultSupersessionAuthorityNotFound, + ValidationResultSupersessionAuthorityReadPort, + ValidationResultSupersessionAuthorityRecord, + ValidationResultSupersessionAuthorityView, + resolve_validation_result_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OTHER_RESULT_REFERENCE = "validation_analysis_result:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +SUCCESSOR_REFERENCE = "validation_analysis_result:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +OTHER_OWNER_CONTRACT_REFERENCE = "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +RESULT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +RELEASED_AT = datetime(2026, 9, 17, 10, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 12, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_result_reference", + "successor_correction_sequence", + "successor_result_digest", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_validation_result_supersession_authority(self, **coordinates: object) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(ValidationResultSupersessionAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that static capability validation must reject.""" + + @property + def read_validation_result_supersession_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-supersession-authority-read-v1", + resource_kind="validation_result_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> ValidationResultSupersessionAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "evidence_version": 1, + "correction_sequence": 2, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + "successor_result_reference": None, + "successor_correction_sequence": None, + "successor_result_digest": None, + "successor_released_at": None, + } + values.update(overrides) + return ValidationResultSupersessionAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> ValidationResultSupersessionAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "evidence_version": 1, + "correction_sequence": 2, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_validation_result_supersession_authority(**values) + + +def test_current_result_resolution_uses_owner_release_chronology_without_successor() -> None: + port = _ReadPort(_record()) + view = _resolve(read_port=port) + + assert isinstance(port, ValidationResultSupersessionAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["result_reference"] == RESULT_REFERENCE + assert "owner_contract_released_at" not in port.calls[0] + assert "released_at" not in port.calls[0] + assert "superseded_at" not in port.calls[0] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["owner_contract_released_at"] == OWNER_CONTRACT_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert "superseded_at" not in fields + assert "successor_result_reference" not in fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(ValidationResultSupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(ValidationResultSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"result_reference": OTHER_RESULT_REFERENCE}, + {"result_digest": "a" * 64}, + {"correction_sequence": 3}, + {"owner_contract_reference": OTHER_OWNER_CONTRACT_REFERENCE}, + {"owner_contract_version": 4}, + {"owner_contract_digest": "b" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(ValidationResultSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_release_chronology_and_historical_use_fail_closed_or_remain_reproducible() -> None: + with pytest.raises(ValidationResultSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + record = _record( + superseded_at=USED_AT + timedelta(seconds=1), + successor_result_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_result_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT + timedelta(seconds=1), + ) + view = _resolve(read_port=_ReadPort(record)) + assert dict(view.fields)["result_digest"] == RESULT_DIGEST + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("result_reference", "wrong:result", ValueError), + ("result_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("evidence_version", 2, ValueError), + ("correction_sequence", 0, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "2" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_rejects_non_v1_evidence_and_public_view_construction() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(TypeError, match="issued only by"): + ValidationResultSupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_record_rejects_naive_owner_resolved_timestamps_and_malformed_successor() -> None: + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 1)) + with pytest.raises(ValueError): + _record(released_at=datetime(2026, 9, 17, 10)) + with pytest.raises(ValueError): + _record( + superseded_at=datetime(2026, 9, 17, 13), + successor_result_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_result_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT, + ) + with pytest.raises(ValueError): + _record( + superseded_at=USED_AT + timedelta(hours=1), + successor_result_reference="analysis_weight_receipt:33333333-3333-4333-8333-333333333333", + successor_correction_sequence=3, + successor_result_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT, + ) + with pytest.raises(ValueError): + _record( + superseded_at=USED_AT + timedelta(hours=1), + successor_result_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=True, + successor_result_digest=SUCCESSOR_DIGEST, + successor_released_at=USED_AT, + ) + with pytest.raises(ValueError): + _record( + superseded_at=USED_AT + timedelta(hours=1), + successor_result_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_result_digest="not-a-digest", + successor_released_at=USED_AT, + ) + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + assert record.successor_fields is None + assert dict(record.fields)["correction_sequence"] == 2 + with pytest.raises(AttributeError): + object.__setattr__(record, "correction_sequence", 3) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) diff --git a/services/workforce-validation-api/tests/test_result_supersession_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_result_supersession_authority_structural_integrity.py new file mode 100644 index 000000000..9d5e7ceea --- /dev/null +++ b/services/workforce-validation-api/tests/test_result_supersession_authority_structural_integrity.py @@ -0,0 +1,127 @@ +"""Regression coverage for canonical validation-result supersession owner structure.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_supersession_authority import ( + ValidationResultSupersessionAuthorityIntegrityError, + ValidationResultSupersessionAuthorityRecord, + resolve_validation_result_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OWNER_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RESULT_DIGEST = "1" * 64 +OWNER_DIGEST = "2" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 10, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 12, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "evidence_version", + "correction_sequence", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_result_reference", + "successor_correction_sequence", + "successor_result_digest", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured owner evidence without normalizing tuple structure.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_validation_result_supersession_authority(self, **_: object) -> object: + """Return the configured raw owner result.""" + return self.result + + +def _record() -> ValidationResultSupersessionAuthorityRecord: + """Build one current canonical validation-result supersession record.""" + return ValidationResultSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def _resolve(read_port: object) -> object: + """Resolve canonical coordinates through a supplied raw owner port.""" + principal = ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-supersession-authority-read-v1", + resource_kind="validation_result_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + return resolve_validation_result_supersession_authority( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_DIGEST, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=read_port, + ) + + +def test_owner_port_cannot_append_hidden_tuple_fields_to_exact_result_supersession_record() -> None: + """Reject exact-typed evidence with coordinates outside the canonical tuple.""" + valid = _record() + forged = tuple.__new__( + ValidationResultSupersessionAuthorityRecord, + (*tuple(valid), "hidden-unreviewed-owner-coordinate"), + ) + + with pytest.raises(ValidationResultSupersessionAuthorityIntegrityError): + _resolve(_ReadPort(forged)) + + +def test_malformed_exact_result_supersession_record_maps_to_integrity_error() -> None: + """Map truncated exact-typed evidence to the domain integrity boundary.""" + valid = _record() + forged = tuple.__new__(ValidationResultSupersessionAuthorityRecord, tuple(valid)[:-1]) + + with pytest.raises(ValidationResultSupersessionAuthorityIntegrityError): + _resolve(_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_result_supersession_cutover_alignment.py b/services/workforce-validation-api/tests/test_result_supersession_cutover_alignment.py new file mode 100644 index 000000000..47aeb0013 --- /dev/null +++ b/services/workforce-validation-api/tests/test_result_supersession_cutover_alignment.py @@ -0,0 +1,55 @@ +"""Require a released successor to become authoritative exactly at predecessor cutover.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_supersession_authority import ( + ValidationResultSupersessionAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +SUCCESSOR_REFERENCE = "validation_analysis_result:33333333-3333-4333-8333-333333333333" +OWNER_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RESULT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_DIGEST = "2" * 64 +RELEASED_AT = datetime(2026, 9, 17, 10, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 17, 12, tzinfo=timezone.utc) + + +def _record(*, successor_released_at: datetime) -> ValidationResultSupersessionAuthorityRecord: + return ValidationResultSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + evidence_version=1, + correction_sequence=2, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=datetime(2026, 9, 1, tzinfo=timezone.utc), + released_at=RELEASED_AT, + superseded_at=CUTOVER, + successor_result_reference=SUCCESSOR_REFERENCE, + successor_correction_sequence=3, + successor_result_digest=SUCCESSOR_DIGEST, + successor_released_at=successor_released_at, + ) + + +def test_successor_release_cannot_precede_cutover() -> None: + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=CUTOVER - timedelta(seconds=1)) + + +def test_successor_release_matches_cutover() -> None: + record = _record(successor_released_at=CUTOVER) + assert record.superseded_at == CUTOVER + assert dict(record.successor_fields or ())["successor_released_at"] == CUTOVER diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_authority.py b/services/workforce-validation-api/tests/test_trimming_bounding_authority.py new file mode 100644 index 000000000..089be20d3 --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_authority.py @@ -0,0 +1,370 @@ +"""Fail-closed contract for released trimming/bounding adjustment authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +import orgmetra_workforce_validation_api.trimming_bounding_authority as authority_module +from orgmetra_workforce_validation_api.trimming_bounding_authority import ( + TrimmingBoundingAuthorityIntegrityError, + TrimmingBoundingAuthorityNotFound, + TrimmingBoundingAuthorityReadPort, + TrimmingBoundingAuthorityRecord, + TrimmingBoundingAuthorityView, + resolve_trimming_bounding_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT_REFERENCE = "trimming_bounding_adjustment_receipt:11111111-1111-4111-8111-111111111111" +RULE_REFERENCE = "weight_trimming_rule:winsor-p995-v1" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RECEIPT_DIGEST = "1" * 64 +RULE_CONFIGURATION_DIGEST = "2" * 64 +AFFECTED_CASE_SET_DIGEST = "3" * 64 +INPUT_WEIGHT_DIGEST = "4" * 64 +OUTPUT_WEIGHT_DIGEST = "5" * 64 +OWNER_CONTRACT_DIGEST = "6" * 64 +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "adjustment_receipt_reference", + "adjustment_receipt_digest", + "evidence_version", + "rule_reference", + "rule_version", + "rule_configuration_digest", + "affected_case_occurrence_set_digest", + "affected_case_count", + "input_weight_artifact_digest", + "output_weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured trimming authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_trimming_bounding_authority(self, **coordinates: object) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(TrimmingBoundingAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must be rejected without executing it.""" + + @property + def read_trimming_bounding_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="trimming-bounding-authority-read-v1", + resource_kind="trimming_bounding_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> TrimmingBoundingAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "adjustment_receipt_reference": RECEIPT_REFERENCE, + "adjustment_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "rule_reference": RULE_REFERENCE, + "rule_version": 2, + "rule_configuration_digest": RULE_CONFIGURATION_DIGEST, + "affected_case_occurrence_set_digest": AFFECTED_CASE_SET_DIGEST, + "affected_case_count": 17, + "input_weight_artifact_digest": INPUT_WEIGHT_DIGEST, + "output_weight_artifact_digest": OUTPUT_WEIGHT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return TrimmingBoundingAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> TrimmingBoundingAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "adjustment_receipt_reference": RECEIPT_REFERENCE, + "adjustment_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "rule_reference": RULE_REFERENCE, + "rule_version": 2, + "rule_configuration_digest": RULE_CONFIGURATION_DIGEST, + "affected_case_occurrence_set_digest": AFFECTED_CASE_SET_DIGEST, + "affected_case_count": 17, + "input_weight_artifact_digest": INPUT_WEIGHT_DIGEST, + "output_weight_artifact_digest": OUTPUT_WEIGHT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_trimming_bounding_authority(**values) + + +def test_resolution_binds_rule_affected_cases_and_artifact_lineage() -> None: + port = _ReadPort(_record()) + view = _resolve(read_port=port) + + assert isinstance(port, TrimmingBoundingAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["rule_reference"] == RULE_REFERENCE + assert port.calls[0]["affected_case_occurrence_set_digest"] == AFFECTED_CASE_SET_DIGEST + assert port.calls[0]["affected_case_count"] == 17 + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert ("rule_reference", RULE_REFERENCE) in view.fields + assert ("rule_version", 2) in view.fields + assert ("affected_case_count", 17) in view.fields + assert ("affected_case_occurrence_set_digest", AFFECTED_CASE_SET_DIGEST) in view.fields + assert ("output_weight_artifact_digest", OUTPUT_WEIGHT_DIGEST) in view.fields + assert ("owner_contract_released_at", OWNER_CONTRACT_RELEASED_AT) in view.fields + assert ("superseded_at", None) in view.fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(TrimmingBoundingAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"adjustment_receipt_digest": "a" * 64}, + {"rule_reference": "weight_trimming_rule:other"}, + {"rule_version": 3}, + {"rule_configuration_digest": "b" * 64}, + {"affected_case_occurrence_set_digest": "c" * 64}, + {"affected_case_count": 18}, + {"input_weight_artifact_digest": "d" * 64}, + {"output_weight_artifact_digest": "e" * 64}, + {"constructed_at": CONSTRUCTED_AT + timedelta(seconds=1)}, + {"owner_contract_version": 4}, + {"owner_contract_digest": "f" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_artifact_release_and_currentness_chronology_fail_closed() -> None: + with pytest.raises(ValueError): + _record(output_weight_artifact_digest=INPUT_WEIGHT_DIGEST) + with pytest.raises(ValueError): + _record(released_at=CONSTRUCTED_AT - timedelta(seconds=1)) + with pytest.raises(ValueError, match="superseded_at must be later"): + _record(superseded_at=RELEASED_AT) + with pytest.raises(ValueError, match="standard-library timezone provider"): + _record(superseded_at=datetime(2026, 9, 16, 14, 0)) + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + cutover = RELEASED_AT + timedelta(hours=1) + historical = _resolve( + read_port=_ReadPort(_record(superseded_at=cutover)), + used_at=cutover - timedelta(seconds=1), + ) + assert ("superseded_at", cutover) in historical.fields + with pytest.raises( + TrimmingBoundingAuthorityIntegrityError, + match="superseded for this scientific-use instant", + ): + _resolve( + read_port=_ReadPort(_record(superseded_at=cutover)), + used_at=cutover, + ) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("adjustment_receipt_reference", "wrong:receipt", ValueError), + ("adjustment_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("rule_reference", "wrong:rule", ValueError), + ("rule_version", 0, ValueError), + ("rule_configuration_digest", "2" * 63, ValueError), + ("affected_case_occurrence_set_digest", "3" * 65, ValueError), + ("affected_case_count", True, ValueError), + ("input_weight_artifact_digest", "4" * 63, ValueError), + ("output_weight_artifact_digest", "5" * 65, ValueError), + ("constructed_at", datetime(2026, 9, 16, 12, 0), ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "6" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(record, "affected_case_count", 18) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + TrimmingBoundingAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_view_rejects_tuple_base_constructor_forgery() -> None: + """Reject caller-authored instances created through the tuple base class.""" + with pytest.raises(TypeError): + tuple.__new__( + TrimmingBoundingAuthorityView, + (TENANT, STUDY, (("affected_case_count", 17),)), + ) + + +def test_raw_view_allocation_cannot_expose_projection_state() -> None: + """Keep an unissued raw allocation unusable through every public property.""" + forged = object.__new__(TrimmingBoundingAuthorityView) + + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _ = forged.tenant_record_id + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _ = forged.validity_study_id + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _ = forged.fields + + +def test_view_rejects_caller_authored_issuance_marker() -> None: + """Reject a raw allocation even when a caller invents a marker value.""" + forged = object.__new__(TrimmingBoundingAuthorityView) + object.__setattr__(forged, "_tenant_identity", TENANT) + object.__setattr__(forged, "_study_identity", STUDY) + object.__setattr__(forged, "_fields", ()) + object.__setattr__(forged, "_issuance_marker", object()) + + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _ = forged.fields + + +def test_importable_marker_cannot_mint_trimming_bounding_view() -> None: + """Keep the trimming/bounding view seal outside importable module state.""" + marker_name = "_TRIMMING_BOUNDING_VIEW_ISSUANCE_MARKER" + assert not hasattr(authority_module, marker_name) + + forged = object.__new__(TrimmingBoundingAuthorityView) + object.__setattr__(forged, "_tenant_identity", TENANT.int) + object.__setattr__(forged, "_study_identity", STUDY.int) + object.__setattr__(forged, "_fields", ()) + object.__setattr__(forged, "_issuance_marker", object()) + + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _ = forged.fields + + +def test_issued_view_rejects_mutation_and_deletion() -> None: + """Keep a resolver-issued view immutable after all owner checks complete.""" + view = _resolve(read_port=_ReadPort(_record())) + + with pytest.raises(AttributeError): + view._fields = () + with pytest.raises(AttributeError): + del view._fields diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_authority_edges.py b/services/workforce-validation-api/tests/test_trimming_bounding_authority_edges.py new file mode 100644 index 000000000..2ec9a17fe --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_authority_edges.py @@ -0,0 +1,42 @@ +"""Hostile edges for released trimming/bounding authority.""" + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.trimming_bounding_authority import ( + TrimmingBoundingAuthorityRecord, +) + + +def _record(*, evidence_version: object = 1) -> TrimmingBoundingAuthorityRecord: + return TrimmingBoundingAuthorityRecord( + tenant_record_id=UUID("10000000-0000-7000-8000-000000000001"), + validity_study_id=UUID("00000000-0000-7000-8000-0000000000d1"), + adjustment_receipt_reference=( + "trimming_bounding_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + adjustment_receipt_digest="1" * 64, + evidence_version=evidence_version, + rule_reference="weight_trimming_rule:winsor-p995-v1", + rule_version=2, + rule_configuration_digest="2" * 64, + affected_case_occurrence_set_digest="3" * 64, + affected_case_count=17, + input_weight_artifact_digest="4" * 64, + output_weight_artifact_digest="5" * 64, + constructed_at=datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc), + owner_contract_reference=( + "released_owner_contract:22222222-2222-4222-8222-222222222222" + ), + owner_contract_version=3, + owner_contract_digest="6" * 64, + owner_contract_released_at=datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc), + released_at=datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc), + ) + + +def test_evidence_version_cannot_advance_without_contract_revision() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_trimming_bounding_authority_structural_integrity.py new file mode 100644 index 000000000..422251fdf --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_authority_structural_integrity.py @@ -0,0 +1,33 @@ +"""Structural-integrity regressions for trimming/bounding owner evidence.""" + +from __future__ import annotations + +import pytest + +from orgmetra_workforce_validation_api.trimming_bounding_authority import ( + TrimmingBoundingAuthorityIntegrityError, + TrimmingBoundingAuthorityRecord, +) +from test_trimming_bounding_authority import _ReadPort, _record, _resolve + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + TrimmingBoundingAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + TrimmingBoundingAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(TrimmingBoundingAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_owner_contract_chronology.py b/services/workforce-validation-api/tests/test_trimming_bounding_owner_contract_chronology.py new file mode 100644 index 000000000..ba18edb4a --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_owner_contract_chronology.py @@ -0,0 +1,75 @@ +"""Fail closed when trimming/bounding owner contracts are retroactive.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.trimming_bounding_authority import ( + TrimmingBoundingAuthorityRecord, + resolve_trimming_bounding_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> TrimmingBoundingAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "adjustment_receipt_reference": ( + "trimming_bounding_adjustment_receipt:11111111-1111-4111-8111-111111111111" + ), + "adjustment_receipt_digest": "1" * 64, + "evidence_version": 1, + "rule_reference": "weight_trimming_rule:winsor-p995-v1", + "rule_version": 2, + "rule_configuration_digest": "2" * 64, + "affected_case_occurrence_set_digest": "3" * 64, + "affected_case_count": 17, + "input_weight_artifact_digest": "4" * 64, + "output_weight_artifact_digest": "5" * 64, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": ( + "released_owner_contract:22222222-2222-4222-8222-222222222222" + ), + "owner_contract_version": 3, + "owner_contract_digest": "6" * 64, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return TrimmingBoundingAuthorityRecord(**values) + + +def test_owner_contract_release_is_owner_resolved_not_a_request_coordinate() -> None: + assert "owner_contract_released_at" not in signature( + resolve_trimming_bounding_authority + ).parameters + + +def test_owner_contract_must_exist_before_adjustment_receipt_release() -> None: + with pytest.raises(ValueError, match="owner_contract_released_at"): + _record( + owner_contract_released_at=datetime( + 2026, 9, 16, 13, 0, 1, tzinfo=timezone.utc + ) + ) + + +def test_owner_contract_release_requires_timezone_aware_evidence() -> None: + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 16, 12, 30)) + + +def test_contract_released_after_construction_but_before_receipt_release_is_valid() -> None: + record = _record() + assert record.owner_contract_released_at == OWNER_CONTRACT_RELEASED_AT + assert record.released_at == RELEASED_AT diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_edges.py b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_edges.py new file mode 100644 index 000000000..ce8f624b0 --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_edges.py @@ -0,0 +1,317 @@ +"""Hostile edges for append-only trimming-bounding correction authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.trimming_bounding_supersession_authority import ( + TrimmingBoundingSupersessionAuthorityIntegrityError, + TrimmingBoundingSupersessionAuthorityNotFound, + TrimmingBoundingSupersessionAuthorityReadPort, + TrimmingBoundingSupersessionAuthorityRecord, + TrimmingBoundingSupersessionAuthorityView, + resolve_trimming_bounding_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT = "trimming_bounding_adjustment_receipt:11111111-1111-4111-8111-111111111111" +OTHER_RECEIPT = "trimming_bounding_adjustment_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +SUCCESSOR = "trimming_bounding_adjustment_receipt:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +OTHER_OWNER = "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "2" * 64 +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 2, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 1, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "adjustment_receipt_reference", + "adjustment_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_adjustment_receipt_reference", + "successor_adjustment_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + """Store one owner result and initialize the call ledger.""" + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_trimming_bounding_supersession_authority( + self, **coordinates: object + ) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately omit the required owner-read capability.""" + + +class _ProtocolOnly(TrimmingBoundingSupersessionAuthorityReadPort): + """Inherit only the Protocol declaration, not a concrete capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that static capability validation must reject.""" + + @property + def read_trimming_bounding_supersession_authority(self) -> object: + """Trip if dependency validation executes the descriptor.""" + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + """Return one exact workforce-validation principal.""" + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + """Return the purpose-bound policy for trimming correction evidence.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="trimming-bounding-supersession-read-v1", + resource_kind="trimming_bounding_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> TrimmingBoundingSupersessionAuthorityRecord: + """Build one current trimming correction state.""" + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "adjustment_receipt_reference": RECEIPT, + "adjustment_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + "successor_adjustment_receipt_reference": None, + "successor_adjustment_receipt_digest": None, + "successor_evidence_version": None, + "successor_released_at": None, + } + values.update(overrides) + return TrimmingBoundingSupersessionAuthorityRecord(**values) + + +def _resolve( + *, read_port: object, **overrides: object +) -> TrimmingBoundingSupersessionAuthorityView: + """Resolve current trimming authority with caller-known coordinates only.""" + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "adjustment_receipt_reference": RECEIPT, + "adjustment_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_trimming_bounding_supersession_authority(**values) + + +def test_current_receipt_resolution_uses_owner_chronology_without_successor() -> None: + """Resolve a current receipt and keep chronology out of the lookup key.""" + port = _ReadPort(_record()) + view = _resolve(read_port=port) + + assert isinstance(port, TrimmingBoundingSupersessionAuthorityReadPort) + assert len(port.calls) == 1 + assert "released_at" not in port.calls[0] + assert "superseded_at" not in port.calls[0] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert dict(view.fields)["released_at"] == RELEASED_AT + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + """Do not consult owner evidence when purpose authorization fails.""" + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + """Reject absent and non-canonical owner evidence.""" + with pytest.raises(TrimmingBoundingSupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(TrimmingBoundingSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"adjustment_receipt_reference": OTHER_RECEIPT}, + {"adjustment_receipt_digest": "a" * 64}, + {"owner_contract_reference": OTHER_OWNER}, + {"owner_contract_version": 2}, + {"owner_contract_digest": "b" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + """Fail closed if owner evidence differs from any requested coordinate.""" + with pytest.raises(TrimmingBoundingSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_release_chronology_and_historical_use_are_distinct() -> None: + """Reject pre-release use while preserving history before a later cutover.""" + with pytest.raises(TrimmingBoundingSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + record = _record( + superseded_at=CUTOVER, + successor_adjustment_receipt_reference=SUCCESSOR, + successor_adjustment_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=CUTOVER, + ) + assert dict(_resolve(read_port=_ReadPort(record)).fields)[ + "adjustment_receipt_digest" + ] == RECEIPT_DIGEST + with pytest.raises(TrimmingBoundingSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(record), used_at=CUTOVER) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("adjustment_receipt_reference", "wrong:receipt", ValueError), + ("adjustment_receipt_digest", "ABC", ValueError), + ("evidence_version", 2, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "2" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + """Validate caller-controlled coordinates before touching the owner port.""" + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_rejects_invalid_chronology_schema_and_public_view() -> None: + """Keep chronology atomic, evidence v1, and minimized views issuer-only.""" + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError, match="later than trimming"): + _record( + superseded_at=RELEASED_AT, + successor_adjustment_receipt_reference=SUCCESSOR, + successor_adjustment_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=RELEASED_AT, + ) + with pytest.raises(ValueError, match="successor_evidence_version"): + _record( + superseded_at=CUTOVER, + successor_adjustment_receipt_reference=SUCCESSOR, + successor_adjustment_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=2, + successor_released_at=CUTOVER, + ) + with pytest.raises(ValueError, match="released after"): + _record( + superseded_at=RELEASED_AT + timedelta(seconds=1), + successor_adjustment_receipt_reference=SUCCESSOR, + successor_adjustment_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=RELEASED_AT, + ) + with pytest.raises(TypeError, match="issued only by"): + TrimmingBoundingSupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_record_and_view_are_immutable_and_uuid_views_detached() -> None: + """Detach UUIDs and reject mutation of owner records and minimized views.""" + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + assert record.successor_fields is None + with pytest.raises(AttributeError): + object.__setattr__(record, "released_at", USED_AT) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_structural_integrity.py new file mode 100644 index 000000000..0564c56e9 --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_structural_integrity.py @@ -0,0 +1,109 @@ +"""Structural-integrity regressions for trimming/bounding supersession evidence.""" + +from __future__ import annotations + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.trimming_bounding_supersession_authority import ( + TrimmingBoundingSupersessionAuthorityIntegrityError, + TrimmingBoundingSupersessionAuthorityRecord, + resolve_trimming_bounding_supersession_authority, +) +from test_trimming_bounding_supersession_contract import _record + +READ_FIELDS = frozenset( + { + "adjustment_receipt_reference", + "adjustment_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_adjustment_receipt_reference", + "successor_adjustment_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured persisted trimming supersession evidence.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_trimming_bounding_supersession_authority(self, **_: object) -> object: + """Return configured owner evidence.""" + return self.result + + +def _resolve(canonical: TrimmingBoundingSupersessionAuthorityRecord, result: object) -> object: + """Resolve the predecessor at a valid pre-cutover use instant.""" + values = dict(canonical.fields) + return resolve_trimming_bounding_supersession_authority( + principal=ValidationPrincipal( + tenant_record_id=canonical.tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=canonical.tenant_record_id, + validity_study_id=canonical.validity_study_id, + adjustment_receipt_reference=values["adjustment_receipt_reference"], + adjustment_receipt_digest=values["adjustment_receipt_digest"], + evidence_version=values["evidence_version"], + owner_contract_reference=values["owner_contract_reference"], + owner_contract_version=values["owner_contract_version"], + owner_contract_digest=values["owner_contract_digest"], + used_at=canonical.released_at, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=canonical.tenant_record_id, + policy_version_code="trimming-bounding-supersession-read-v1", + resource_kind="trimming_bounding_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + TrimmingBoundingSupersessionAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(TrimmingBoundingSupersessionAuthorityIntegrityError): + _resolve(canonical, forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + TrimmingBoundingSupersessionAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(TrimmingBoundingSupersessionAuthorityIntegrityError): + _resolve(canonical, forged) + + +def test_duplicate_nested_current_field_cannot_be_normalized_away() -> None: + canonical = _record() + raw = list(canonical) + raw[2] = canonical.fields + ( + ("adjustment_receipt_reference", dict(canonical.fields)["adjustment_receipt_reference"]), + ) + forged = tuple.__new__(TrimmingBoundingSupersessionAuthorityRecord, tuple(raw)) + + with pytest.raises(TrimmingBoundingSupersessionAuthorityIntegrityError): + _resolve(canonical, forged) diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..6541776ab --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for trimming/bounding supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.trimming_bounding_supersession_authority import ( + TrimmingBoundingSupersessionAuthorityIntegrityError, + TrimmingBoundingSupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_low_level_tuple_construction_cannot_issue_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + TrimmingBoundingSupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("adjustment_receipt_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(TrimmingBoundingSupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + TrimmingBoundingSupersessionAuthorityIntegrityError, + match="was not issued by resolve_trimming_bounding_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(TrimmingBoundingSupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + TrimmingBoundingSupersessionAuthorityIntegrityError, + match="was not issued by resolve_trimming_bounding_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(TrimmingBoundingSupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..5989078b2 --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_authority_view_seal_capability.py @@ -0,0 +1,58 @@ +"""Hostile sealing-capability regression for trimming/bounding supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.trimming_bounding_supersession_authority as authority_module +from orgmetra_workforce_validation_api.trimming_bounding_supersession_authority import ( + TrimmingBoundingSupersessionAuthorityIntegrityError, + TrimmingBoundingSupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000425") +STUDY = UUID("00000000-0000-0000-0000-000000000426") + + +def _raw_view_with_module_marker() -> TrimmingBoundingSupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(TrimmingBoundingSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__( + view, + "_fields", + (("adjustment_receipt_digest", "b" * 64),), + ) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_TRIMMING_BOUNDING_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_trimming_bounding_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_TRIMMING_BOUNDING_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_trimming_bounding_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + TrimmingBoundingSupersessionAuthorityIntegrityError, + match=( + "trimming/bounding supersession view was not issued by " + "resolve_trimming_bounding_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_trimming_bounding_supersession_contract.py b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_contract.py new file mode 100644 index 000000000..37c9c8285 --- /dev/null +++ b/services/workforce-validation-api/tests/test_trimming_bounding_supersession_contract.py @@ -0,0 +1,76 @@ +"""Regression contract for append-only trimming/bounding corrections.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import uuid4 + +import pytest + +from orgmetra_workforce_validation_api.trimming_bounding_authority import ( + TrimmingBoundingAuthorityReadPort, + TrimmingBoundingAuthorityRecord, +) +from orgmetra_workforce_validation_api.trimming_bounding_supersession_authority import ( + TrimmingBoundingSupersessionAuthorityRecord, +) + + +def _released_at() -> datetime: + """Return a stable aware instant for correction chronology.""" + return datetime(2026, 9, 18, 1, 30, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> TrimmingBoundingSupersessionAuthorityRecord: + """Build one predecessor/successor edge with an atomic cutover.""" + released_at = _released_at() + values: dict[str, object] = { + "tenant_record_id": uuid4(), + "validity_study_id": uuid4(), + "adjustment_receipt_reference": "trimming_bounding_adjustment_receipt:old", + "adjustment_receipt_digest": "a" * 64, + "evidence_version": 1, + "owner_contract_reference": "released_owner_contract:weighting-v1", + "owner_contract_version": 1, + "owner_contract_digest": "b" * 64, + "owner_contract_released_at": released_at - timedelta(minutes=5), + "released_at": released_at, + "superseded_at": released_at + timedelta(minutes=10), + "successor_adjustment_receipt_reference": "trimming_bounding_adjustment_receipt:new", + "successor_adjustment_receipt_digest": "c" * 64, + "successor_evidence_version": 1, + "successor_released_at": released_at + timedelta(minutes=10), + } + values.update(overrides) + return TrimmingBoundingSupersessionAuthorityRecord(**values) + + +def test_ordinary_trimming_authority_keeps_cutover_owner_resolved() -> None: + """Currentness has a cutover without turning chronology into lookup identity.""" + assert "superseded_at" in signature(TrimmingBoundingAuthorityRecord).parameters + read_parameters = signature( + TrimmingBoundingAuthorityReadPort.read_trimming_bounding_authority + ).parameters + assert "superseded_at" not in read_parameters + assert "owner_contract_released_at" not in read_parameters + assert "released_at" not in read_parameters + + +def test_trimming_supersession_requires_atomic_successor_release() -> None: + """A correction edge cannot create an overlap or gap around cutover.""" + _record() + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=_released_at() + timedelta(minutes=11)) + + +def test_trimming_supersession_requires_complete_new_successor() -> None: + """A cutover must identify one complete new immutable successor receipt.""" + with pytest.raises(ValueError, match="complete released successor"): + _record(successor_released_at=None) + with pytest.raises(ValueError, match="new reference"): + _record( + successor_adjustment_receipt_reference="trimming_bounding_adjustment_receipt:old" + ) + with pytest.raises(ValueError, match="new evidence"): + _record(successor_adjustment_receipt_digest="a" * 64) diff --git a/services/workforce-validation-api/tests/test_uuid_payload_integrity.py b/services/workforce-validation-api/tests/test_uuid_payload_integrity.py new file mode 100644 index 000000000..5b20e787e --- /dev/null +++ b/services/workforce-validation-api/tests/test_uuid_payload_integrity.py @@ -0,0 +1,30 @@ +"""Regression contract for exact UUID payload validation before sentinel comparison.""" + +from __future__ import annotations + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.registry import ValidationPrincipal + + +class _ExecutableUUIDPayload: + """Fail if validation compares a forged UUID payload before proving it is an int.""" + + def __eq__(self, other: object) -> bool: + """Expose equality execution as a trust-boundary violation.""" + raise AssertionError(f"forged UUID payload executed equality against {other!r}") + + +def test_exact_uuid_with_executable_internal_payload_fails_before_comparison() -> None: + """Exact UUID outer type cannot authorize executable non-integer internal storage.""" + tenant_record_id = UUID("10000000-0000-7000-8000-000000000001") + object.__setattr__(tenant_record_id, "int", _ExecutableUUIDPayload()) + + with pytest.raises(ValueError, match="tenant_record_id must be an exact operational UUID"): + ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) diff --git a/services/workforce-validation-api/tests/test_uuid_storage_integrity.py b/services/workforce-validation-api/tests/test_uuid_storage_integrity.py new file mode 100644 index 000000000..52c23998e --- /dev/null +++ b/services/workforce-validation-api/tests/test_uuid_storage_integrity.py @@ -0,0 +1,161 @@ +"""Regression contract for UUID storage behind immutable registry value objects.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.registry import ( + ValidationPrincipal, + ValidityStudyIntegrityError, + ValidityStudyRecord, + read_validity_study, +) + +TENANT_TEXT = "10000000-0000-7000-8000-000000000001" +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY_TEXT = "00000000-0000-7000-8000-0000000000c1" +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000c2") +CRITERION_TEXT = "00000000-0000-7000-8000-0000000000a1" +OTHER_CRITERION = UUID("00000000-0000-7000-8000-0000000000a2") +RECORDED_FROM = datetime(2026, 11, 3, tzinfo=timezone.utc) + + +class _ReadPort: + """Return one configured owner record for UUID-storage regression coverage.""" + + def __init__(self, result: ValidityStudyRecord) -> None: + self.result = result + + def read_validity_study( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + ) -> ValidityStudyRecord: + """Return the configured record after the application boundary authorizes the read.""" + return self.result + + +class _TargetSwitchingReadPort: + """Attempt to rewrite the authorized UUID target during the executable port call.""" + + def read_validity_study( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + ) -> ValidityStudyRecord: + """Mutate received UUID aliases and return a record matching the rewritten target.""" + object.__setattr__(tenant_record_id, "int", OTHER_TENANT.int) + object.__setattr__(validity_study_id, "int", OTHER_STUDY.int) + return ValidityStudyRecord( + tenant_record_id=OTHER_TENANT, + validity_study_id=OTHER_STUDY, + criterion_blueprint_id=UUID(CRITERION_TEXT), + study_status_code="study_draft", + recorded_from=RECORDED_FROM, + recorded_to=None, + ) + + +def _policy() -> PurposeBoundAccessPolicy: + """Return the canonical purpose-bound policy for the regression read.""" + return PurposeBoundAccessPolicy( + tenant_record_id=UUID(TENANT_TEXT), + policy_version_code="validation-read-v1", + resource_kind="validity_study_record", + purpose_code="validation_review", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset({"criterion_blueprint_id"}), + ) + + +def _principal() -> ValidationPrincipal: + """Return one canonical principal for UUID target-integrity tests.""" + return ValidationPrincipal( + tenant_record_id=UUID(TENANT_TEXT), + actor_reference="person:analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def test_principal_and_record_do_not_retain_mutable_uuid_inputs() -> None: + """Retained UUID aliases cannot rewrite identity evidence after constructor validation.""" + tenant = UUID(TENANT_TEXT) + study = UUID(STUDY_TEXT) + criterion = UUID(CRITERION_TEXT) + principal = ValidationPrincipal( + tenant_record_id=tenant, + actor_reference="person:analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + record = ValidityStudyRecord( + tenant_record_id=tenant, + validity_study_id=study, + criterion_blueprint_id=criterion, + study_status_code="study_draft", + recorded_from=RECORDED_FROM, + recorded_to=None, + ) + + object.__setattr__(tenant, "int", OTHER_TENANT.int) + object.__setattr__(study, "int", OTHER_STUDY.int) + object.__setattr__(criterion, "int", OTHER_CRITERION.int) + + assert principal.tenant_record_id == UUID(TENANT_TEXT) + assert record.tenant_record_id == UUID(TENANT_TEXT) + assert record.validity_study_id == UUID(STUDY_TEXT) + assert record.criterion_blueprint_id == UUID(CRITERION_TEXT) + + +def test_port_cannot_switch_the_authorized_target_by_mutating_received_uuid_objects() -> None: + """The target comparison uses pre-port immutable identity evidence, not mutable aliases.""" + with pytest.raises(ValidityStudyIntegrityError, match="another target"): + read_validity_study( + principal=_principal(), + tenant_record_id=UUID(TENANT_TEXT), + validity_study_id=UUID(STUDY_TEXT), + purpose_code="validation_review", + requested_fields=frozenset({"criterion_blueprint_id"}), + policy=_policy(), + read_port=_TargetSwitchingReadPort(), + ) + + +def test_authorized_view_does_not_retain_or_expose_mutable_uuid_storage() -> None: + """Target and projected UUID evidence remain stable across retained-reference rewrites.""" + tenant = UUID(TENANT_TEXT) + study = UUID(STUDY_TEXT) + record = ValidityStudyRecord( + tenant_record_id=UUID(TENANT_TEXT), + validity_study_id=UUID(STUDY_TEXT), + criterion_blueprint_id=UUID(CRITERION_TEXT), + study_status_code="study_draft", + recorded_from=RECORDED_FROM, + recorded_to=None, + ) + + view = read_validity_study( + principal=_principal(), + tenant_record_id=tenant, + validity_study_id=study, + purpose_code="validation_review", + requested_fields=frozenset({"criterion_blueprint_id"}), + policy=_policy(), + read_port=_ReadPort(record), + ) + + object.__setattr__(tenant, "int", OTHER_TENANT.int) + object.__setattr__(study, "int", OTHER_STUDY.int) + projected_criterion = dict(view.fields)["criterion_blueprint_id"] + assert type(projected_criterion) is UUID + object.__setattr__(projected_criterion, "int", OTHER_CRITERION.int) + + assert view.tenant_record_id == UUID(TENANT_TEXT) + assert view.validity_study_id == UUID(STUDY_TEXT) + assert dict(view.fields)["criterion_blueprint_id"] == UUID(CRITERION_TEXT) diff --git a/services/workforce-validation-api/tests/test_validation_result_authority.py b/services/workforce-validation-api/tests/test_validation_result_authority.py new file mode 100644 index 000000000..c075bf942 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_authority.py @@ -0,0 +1,281 @@ +"""Fail closed when a released result is not bound to exact weight/variance evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_authority import ( + ValidationResultAuthorityIntegrityError, + ValidationResultAuthorityNotFound, + ValidationResultAuthorityReadPort, + ValidationResultAuthorityRecord, + ValidationResultAuthorityView, + resolve_validation_result_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +COMPATIBILITY_REFERENCE = ( + "weight_variance_compatibility_receipt:22222222-2222-4222-8222-222222222222" +) +OWNER_REFERENCE = "released_owner_contract:33333333-3333-4333-8333-333333333333" +RESULT_DIGEST = "1" * 64 +COMPATIBILITY_DIGEST = "2" * 64 +ANALYSIS_WEIGHT_DIGEST = "3" * 64 +VARIANCE_DIGEST = "4" * 64 +OWNER_DIGEST = "5" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 4, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 5, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "compatibility_receipt_reference", + "compatibility_receipt_digest", + "analysis_weight_receipt_digest", + "variance_design_receipt_digest", + "verification_status", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return one configured released result record and retain exact lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[tuple[object, ...]] = [] + + def read_validation_result_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + compatibility_receipt_reference: str, + compatibility_receipt_digest: str, + analysis_weight_receipt_digest: str, + variance_design_receipt_digest: str, + verification_status: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> object: + self.calls.append( + ( + tenant_record_id, + validity_study_id, + result_reference, + result_digest, + compatibility_receipt_reference, + compatibility_receipt_digest, + analysis_weight_receipt_digest, + variance_design_receipt_digest, + verification_status, + owner_contract_reference, + owner_contract_version, + owner_contract_digest, + ) + ) + return self.result + + +class _ProtocolOnly(ValidationResultAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-authority-read-v2", + resource_kind="validation_result_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> ValidationResultAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "compatibility_receipt_reference": COMPATIBILITY_REFERENCE, + "compatibility_receipt_digest": COMPATIBILITY_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "verification_status": "verification_pending", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return ValidationResultAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> ValidationResultAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "compatibility_receipt_reference": COMPATIBILITY_REFERENCE, + "compatibility_receipt_digest": COMPATIBILITY_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "verification_status": "verification_pending", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_validation_result_authority(**values) + + +def test_resolution_binds_result_to_exact_compatibility_and_owner_evidence() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, ValidationResultAuthorityReadPort) + assert port.calls == [ + ( + TENANT, + STUDY, + RESULT_REFERENCE, + RESULT_DIGEST, + COMPATIBILITY_REFERENCE, + COMPATIBILITY_DIGEST, + ANALYSIS_WEIGHT_DIGEST, + VARIANCE_DIGEST, + "verification_pending", + OWNER_REFERENCE, + 7, + OWNER_DIGEST, + ) + ] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["result_digest"] == RESULT_DIGEST + assert fields["compatibility_receipt_digest"] == COMPATIBILITY_DIGEST + assert fields["analysis_weight_receipt_digest"] == ANALYSIS_WEIGHT_DIGEST + assert fields["variance_design_receipt_digest"] == VARIANCE_DIGEST + assert fields["verification_status"] == "verification_pending" + assert fields["owner_contract_digest"] == OWNER_DIGEST + assert fields["owner_contract_released_at"] == OWNER_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert fields["superseded_at"] is None + + +def test_not_verifiable_result_remains_released_non_authorizing_evidence() -> None: + record = _record(verification_status="not_verifiable") + view = _resolve( + read_port=_ReadPort(record), + verification_status="not_verifiable", + ) + assert dict(view.fields)["verification_status"] == "not_verifiable" + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_noncanonical_or_mismatched_owner_evidence_fails_closed() -> None: + with pytest.raises(ValidationResultAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(ValidationResultAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + with pytest.raises(ValidationResultAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(result_digest="a" * 64))) + + +def test_distinct_evidence_digests_and_non_authorizing_status_are_required() -> None: + with pytest.raises(ValueError): + _record(compatibility_receipt_digest=RESULT_DIGEST) + with pytest.raises(ValueError): + _resolve( + read_port=_ReadPort(_record()), + variance_design_receipt_digest=ANALYSIS_WEIGHT_DIGEST, + ) + with pytest.raises(ValueError): + _record(verification_status=1) + with pytest.raises(ValueError): + _record(verification_status="verified") + with pytest.raises(ValueError, match="superseded_at"): + _record(superseded_at=RELEASED_AT) + + +def test_invalid_dependencies_and_pre_release_use_fail_closed() -> None: + with pytest.raises(TypeError): + _resolve(read_port=object()) + with pytest.raises(TypeError): + _resolve(read_port=_ProtocolOnly()) + with pytest.raises(TypeError): + _resolve(read_port=_ReadPort(_record()), principal=object()) + with pytest.raises(TypeError): + _resolve(read_port=_ReadPort(_record()), policy=object()) + + port = _ReadPort(_record()) + with pytest.raises(ValidationResultAuthorityIntegrityError): + _resolve( + read_port=port, + used_at=datetime(2026, 9, 17, 4, 59, tzinfo=timezone.utc), + ) + assert len(port.calls) == 1 + + +def test_record_and_view_are_immutable_and_public_view_construction_is_blocked() -> None: + record = _record() + with pytest.raises(AttributeError): + object.__setattr__(record, "verification_status", "not_verifiable") + + view = _resolve(read_port=_ReadPort(record)) + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + ValidationResultAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_cross_tenant_owner_evidence_is_rejected() -> None: + with pytest.raises(ValidationResultAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(tenant_record_id=OTHER_TENANT))) diff --git a/services/workforce-validation-api/tests/test_validation_result_authority_chronology.py b/services/workforce-validation-api/tests/test_validation_result_authority_chronology.py new file mode 100644 index 000000000..ef4dcc6fb --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_authority_chronology.py @@ -0,0 +1,139 @@ +"""Reject retroactive owner contracts and stale validation-result use.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_authority import ( + ValidationResultAuthorityIntegrityError, + ValidationResultAuthorityRecord, + resolve_validation_result_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +COMPATIBILITY_REFERENCE = ( + "weight_variance_compatibility_receipt:22222222-2222-4222-8222-222222222222" +) +OWNER_REFERENCE = "released_owner_contract:33333333-3333-4333-8333-333333333333" +RESULT_DIGEST = "1" * 64 +COMPATIBILITY_DIGEST = "2" * 64 +ANALYSIS_WEIGHT_DIGEST = "3" * 64 +VARIANCE_DIGEST = "4" * 64 +OWNER_DIGEST = "5" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 4, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 5, 0, tzinfo=timezone.utc) +SUPERSEDED_AT = datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "compatibility_receipt_reference", + "compatibility_receipt_digest", + "analysis_weight_receipt_digest", + "variance_design_receipt_digest", + "verification_status", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + def __init__(self, record: ValidationResultAuthorityRecord) -> None: + self.record = record + + def read_validation_result_authority(self, **_: object) -> ValidationResultAuthorityRecord: + return self.record + + +def _record(**overrides: object) -> ValidationResultAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "compatibility_receipt_reference": COMPATIBILITY_REFERENCE, + "compatibility_receipt_digest": COMPATIBILITY_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "verification_status": "verification_pending", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": SUPERSEDED_AT, + } + values.update(overrides) + return ValidationResultAuthorityRecord(**values) + + +def _resolve(*, used_at: datetime) -> object: + return resolve_validation_result_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + compatibility_receipt_reference=COMPATIBILITY_REFERENCE, + compatibility_receipt_digest=COMPATIBILITY_DIGEST, + analysis_weight_receipt_digest=ANALYSIS_WEIGHT_DIGEST, + variance_design_receipt_digest=VARIANCE_DIGEST, + verification_status="verification_pending", + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-authority-read-v2", + resource_kind="validation_result_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(_record()), + ) + + +def test_chronology_is_owner_evidence_not_caller_input() -> None: + parameters = signature(resolve_validation_result_authority).parameters + assert "owner_contract_released_at" not in parameters + assert "superseded_at" not in parameters + + +def test_owner_contract_cannot_retroactively_authorize_released_result() -> None: + with pytest.raises(ValueError, match="owner contract"): + _record( + owner_contract_released_at=datetime( + 2026, 9, 17, 5, 1, tzinfo=timezone.utc + ) + ) + + +def test_result_binding_uses_same_half_open_authority_interval_as_supersession() -> None: + historical = _resolve(used_at=datetime(2026, 9, 17, 6, 59, tzinfo=timezone.utc)) + fields = dict(historical.fields) + assert fields["owner_contract_released_at"] == OWNER_RELEASED_AT + assert fields["superseded_at"] == SUPERSEDED_AT + + with pytest.raises(ValidationResultAuthorityIntegrityError, match="supersession"): + _resolve(used_at=SUPERSEDED_AT) diff --git a/services/workforce-validation-api/tests/test_validation_result_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_validation_result_authority_structural_integrity.py new file mode 100644 index 000000000..f524edde8 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_authority_structural_integrity.py @@ -0,0 +1,138 @@ +"""Regression coverage for canonical validation-result owner evidence structure.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_authority import ( + ValidationResultAuthorityIntegrityError, + ValidationResultAuthorityRecord, + resolve_validation_result_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +COMPATIBILITY_REFERENCE = ( + "weight_variance_compatibility_receipt:22222222-2222-4222-8222-222222222222" +) +OWNER_REFERENCE = "released_owner_contract:33333333-3333-4333-8333-333333333333" +RESULT_DIGEST = "1" * 64 +COMPATIBILITY_DIGEST = "2" * 64 +ANALYSIS_WEIGHT_DIGEST = "3" * 64 +VARIANCE_DIGEST = "4" * 64 +OWNER_DIGEST = "5" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 4, 0, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 5, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "compatibility_receipt_reference", + "compatibility_receipt_digest", + "analysis_weight_receipt_digest", + "variance_design_receipt_digest", + "verification_status", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured owner evidence without normalizing its tuple structure.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_validation_result_authority(self, **_: object) -> object: + """Return the configured raw owner result.""" + return self.result + + +def _record() -> ValidationResultAuthorityRecord: + """Build one valid canonical validation-result authority record.""" + return ValidationResultAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + compatibility_receipt_reference=COMPATIBILITY_REFERENCE, + compatibility_receipt_digest=COMPATIBILITY_DIGEST, + analysis_weight_receipt_digest=ANALYSIS_WEIGHT_DIGEST, + variance_design_receipt_digest=VARIANCE_DIGEST, + verification_status="verification_pending", + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def _resolve(read_port: object) -> object: + """Resolve canonical coordinates through a supplied raw owner port.""" + principal = ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-authority-read-v2", + resource_kind="validation_result_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + return resolve_validation_result_authority( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + compatibility_receipt_reference=COMPATIBILITY_REFERENCE, + compatibility_receipt_digest=COMPATIBILITY_DIGEST, + analysis_weight_receipt_digest=ANALYSIS_WEIGHT_DIGEST, + variance_design_receipt_digest=VARIANCE_DIGEST, + verification_status="verification_pending", + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=read_port, + ) + + +def test_owner_port_cannot_append_hidden_tuple_fields_to_exact_result_record() -> None: + """Reject exact-typed evidence with coordinates outside the canonical tuple.""" + valid = _record() + forged = tuple.__new__( + ValidationResultAuthorityRecord, + (*tuple(valid), "hidden-unreviewed-owner-coordinate"), + ) + + with pytest.raises(ValidationResultAuthorityIntegrityError): + _resolve(_ReadPort(forged)) + + +def test_malformed_exact_result_record_maps_to_integrity_error() -> None: + """Map a truncated exact-typed owner tuple to the domain integrity boundary.""" + valid = _record() + forged = tuple.__new__(ValidationResultAuthorityRecord, tuple(valid)[:-1]) + + with pytest.raises(ValidationResultAuthorityIntegrityError): + _resolve(_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_validation_result_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_validation_result_authority_view_issuance_integrity.py new file mode 100644 index 000000000..49645366c --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_authority_view_issuance_integrity.py @@ -0,0 +1,77 @@ +"""Regression contract for validation-result view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.result_authority as authority_module +from orgmetra_workforce_validation_api.result_authority import ( + ValidationResultAuthorityIntegrityError, + ValidationResultAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_importable_marker_cannot_mint_validation_result_view() -> None: + """Keep the validation-result view seal outside importable module state.""" + marker_name = "_VALIDATION_RESULT_VIEW_ISSUANCE_MARKER" + assert not hasattr(authority_module, marker_name) + + forged_view = object.__new__(ValidationResultAuthorityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__(forged_view, "_fields", ()) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises(ValidationResultAuthorityIntegrityError): + _ = forged_view.fields + + +def test_low_level_tuple_construction_cannot_issue_validation_result_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + ValidationResultAuthorityView, + ( + TENANT.int, + STUDY.int, + (("result_digest", "0" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_validation_result_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(ValidationResultAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + ValidationResultAuthorityIntegrityError, + match="was not issued by resolve_validation_result_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_validation_result_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(ValidationResultAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + ValidationResultAuthorityIntegrityError, + match="was not issued by resolve_validation_result_authority", + ): + _ = forged_view.fields + + +def test_raw_validation_result_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(ValidationResultAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability.py new file mode 100644 index 000000000..1c57e8993 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability.py @@ -0,0 +1,416 @@ +"""RED contract for durable non-verifiability of validation-result evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityIntegrityError, + ValidationResultNonVerifiabilityNotFound, + ValidationResultNonVerifiabilityReadPort, + ValidationResultNonVerifiabilityRecord, + ValidationResultNonVerifiabilityView, + resolve_validation_result_nonverifiability, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +FAILED_WEIGHT_REFERENCE = "analysis_weight_receipt:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = ( + "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +) +OWNER_REFERENCE = "released_owner_contract:44444444-4444-4444-8444-444444444444" +RESULT_DIGEST = "1" * 64 +FAILED_WEIGHT_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +OWNER_DIGEST = "4" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +FAILED_EVIDENCE_RELEASED_AT = datetime(2026, 9, 17, 5, 59, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 6, 10, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "verification_status", + "failed_evidence_kind", + "failure_mode", + "failed_evidence_reference", + "failed_evidence_digest", + "failed_evidence_released_at", + "verification_attempt_reference", + "verification_attempt_digest", + "verification_attempt_released_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "evaluated_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return one configured owner outcome and retain exact lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[tuple[object, ...]] = [] + + def read_validation_result_nonverifiability( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + failed_evidence_reference: str | None, + failed_evidence_digest: str | None, + verification_attempt_reference: str, + verification_attempt_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> object: + self.calls.append( + ( + tenant_record_id, + validity_study_id, + result_reference, + result_digest, + failed_evidence_kind, + failure_mode, + failed_evidence_reference, + failed_evidence_digest, + verification_attempt_reference, + verification_attempt_digest, + owner_contract_reference, + owner_contract_version, + owner_contract_digest, + ) + ) + return self.result + + +class _ProtocolOnly(ValidationResultNonVerifiabilityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-read-v1", + resource_kind="validation_result_nonverifiability", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> ValidationResultNonVerifiabilityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "missing", + "failed_evidence_reference": None, + "failed_evidence_digest": None, + "failed_evidence_released_at": None, + "verification_attempt_reference": ATTEMPT_REFERENCE, + "verification_attempt_digest": ATTEMPT_DIGEST, + "verification_attempt_released_at": ATTEMPT_RELEASED_AT, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "evaluated_at": EVALUATED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return ValidationResultNonVerifiabilityRecord(**values) + + +def _resolve( + *, read_port: object, **overrides: object +) -> ValidationResultNonVerifiabilityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "missing", + "failed_evidence_reference": None, + "failed_evidence_digest": None, + "verification_attempt_reference": ATTEMPT_REFERENCE, + "verification_attempt_digest": ATTEMPT_DIGEST, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_validation_result_nonverifiability(**values) + + +def test_missing_final_weight_evidence_is_released_as_not_verifiable() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, ValidationResultNonVerifiabilityReadPort) + assert port.calls == [ + ( + TENANT, + STUDY, + RESULT_REFERENCE, + RESULT_DIGEST, + "analysis_weight_receipt", + "missing", + None, + None, + ATTEMPT_REFERENCE, + ATTEMPT_DIGEST, + OWNER_REFERENCE, + 7, + OWNER_DIGEST, + ) + ] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["verification_status"] == "not_verifiable" + assert fields["failed_evidence_kind"] == "analysis_weight_receipt" + assert fields["failure_mode"] == "missing" + assert fields["failed_evidence_reference"] is None + assert fields["failed_evidence_digest"] is None + assert fields["failed_evidence_released_at"] is None + assert fields["verification_attempt_reference"] == ATTEMPT_REFERENCE + assert fields["verification_attempt_digest"] == ATTEMPT_DIGEST + assert fields["verification_attempt_released_at"] == ATTEMPT_RELEASED_AT + assert fields["owner_contract_released_at"] == OWNER_CONTRACT_RELEASED_AT + assert fields["evaluated_at"] == EVALUATED_AT + assert fields["released_at"] == RELEASED_AT + assert fields["superseded_at"] is None + + +def test_non_reproducible_weight_evidence_keeps_exact_failed_receipt() -> None: + record = _record( + failure_mode="non_reproducible", + failed_evidence_reference=FAILED_WEIGHT_REFERENCE, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + failed_evidence_released_at=FAILED_EVIDENCE_RELEASED_AT, + ) + port = _ReadPort(record) + + view = _resolve( + read_port=port, + failure_mode="non_reproducible", + failed_evidence_reference=FAILED_WEIGHT_REFERENCE, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + ) + + assert port.calls[0][6:8] == (FAILED_WEIGHT_REFERENCE, FAILED_WEIGHT_DIGEST) + fields = dict(view.fields) + assert fields["verification_status"] == "not_verifiable" + assert fields["failed_evidence_reference"] == FAILED_WEIGHT_REFERENCE + assert fields["failed_evidence_digest"] == FAILED_WEIGHT_DIGEST + assert fields["failed_evidence_released_at"] == FAILED_EVIDENCE_RELEASED_AT + + +@pytest.mark.parametrize( + ("failed_evidence_kind", "failed_reference"), + [ + ( + "weight_variance_compatibility_receipt", + "weight_variance_compatibility_receipt:" + "55555555-5555-4555-8555-555555555555", + ), + ( + "variance_design_receipt", + "variance_design_receipt:66666666-6666-4666-8666-666666666666", + ), + ], +) +def test_non_reproducible_evidence_kind_uses_its_typed_reference( + failed_evidence_kind: str, + failed_reference: str, +) -> None: + record = _record( + failed_evidence_kind=failed_evidence_kind, + failure_mode="non_reproducible", + failed_evidence_reference=failed_reference, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + failed_evidence_released_at=FAILED_EVIDENCE_RELEASED_AT, + ) + view = _resolve( + read_port=_ReadPort(record), + failed_evidence_kind=failed_evidence_kind, + failure_mode="non_reproducible", + failed_evidence_reference=failed_reference, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + ) + assert dict(view.fields)["failed_evidence_reference"] == failed_reference + + +def test_missing_and_non_reproducible_modes_fail_closed_on_incoherent_evidence() -> None: + with pytest.raises(ValueError, match="must be absent"): + _record( + failed_evidence_reference=FAILED_WEIGHT_REFERENCE, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + ) + with pytest.raises(ValueError, match="required"): + _record(failure_mode="non_reproducible") + with pytest.raises(ValueError, match="failed_evidence_reference"): + _record( + failure_mode="non_reproducible", + failed_evidence_reference=( + "variance_design_receipt:22222222-2222-4222-8222-222222222222" + ), + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + failed_evidence_released_at=FAILED_EVIDENCE_RELEASED_AT, + ) + with pytest.raises(ValueError, match="required"): + _resolve( + read_port=_ReadPort( + _record( + failure_mode="non_reproducible", + failed_evidence_reference=FAILED_WEIGHT_REFERENCE, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + failed_evidence_released_at=FAILED_EVIDENCE_RELEASED_AT, + ) + ), + failure_mode="non_reproducible", + ) + with pytest.raises(ValueError, match="must be absent"): + _resolve( + read_port=_ReadPort(_record()), + failed_evidence_reference=FAILED_WEIGHT_REFERENCE, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + ) + + +def test_reason_and_attempt_evidence_are_strict_and_non_aliasing() -> None: + with pytest.raises(ValueError, match="failed_evidence_kind"): + _record(failed_evidence_kind="point_weight") + with pytest.raises(ValueError, match="failure_mode"): + _record(failure_mode="unknown") + with pytest.raises(ValueError, match="verification_attempt_reference"): + _record(verification_attempt_reference="verification-attempt-v1") + with pytest.raises(ValueError, match="verification_attempt_digest"): + _record(verification_attempt_digest="not-a-digest") + with pytest.raises(ValueError, match="must be distinct"): + _record(verification_attempt_digest=RESULT_DIGEST) + + +def test_evaluation_must_precede_release() -> None: + with pytest.raises(ValueError, match="evaluated_at"): + _record( + evaluated_at=datetime(2026, 9, 17, 6, 6, tzinfo=timezone.utc), + verification_attempt_released_at=datetime( + 2026, 9, 17, 6, 6, tzinfo=timezone.utc + ), + released_at=RELEASED_AT, + ) + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_noncanonical_or_mismatched_owner_outcome_fails_closed() -> None: + with pytest.raises(ValidationResultNonVerifiabilityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve(read_port=_ReadPort(object())) + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve(read_port=_ReadPort(_record(result_digest="a" * 64))) + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve(read_port=_ReadPort(_record(verification_attempt_digest="a" * 64))) + + non_reproducible = _record( + failure_mode="non_reproducible", + failed_evidence_reference=FAILED_WEIGHT_REFERENCE, + failed_evidence_digest=FAILED_WEIGHT_DIGEST, + failed_evidence_released_at=FAILED_EVIDENCE_RELEASED_AT, + ) + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve( + read_port=_ReadPort(non_reproducible), + failure_mode="non_reproducible", + failed_evidence_reference=( + "analysis_weight_receipt:77777777-7777-4777-8777-777777777777" + ), + failed_evidence_digest="7" * 64, + ) + + +def test_invalid_dependencies_and_pre_release_use_fail_closed() -> None: + with pytest.raises(TypeError): + _resolve(read_port=object()) + with pytest.raises(TypeError): + _resolve(read_port=_ProtocolOnly()) + with pytest.raises(TypeError): + _resolve(read_port=_ReadPort(_record()), principal=object()) + with pytest.raises(TypeError): + _resolve(read_port=_ReadPort(_record()), policy=object()) + + port = _ReadPort(_record()) + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve( + read_port=port, + used_at=datetime(2026, 9, 17, 6, 4, tzinfo=timezone.utc), + ) + assert len(port.calls) == 1 + + +def test_record_and_view_are_immutable_and_public_view_construction_is_blocked() -> None: + record = _record() + with pytest.raises(AttributeError): + object.__setattr__(record, "failure_mode", "non_reproducible") + + view = _resolve(read_port=_ReadPort(record)) + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + ValidationResultNonVerifiabilityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_cross_tenant_owner_outcome_is_rejected() -> None: + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve(read_port=_ReadPort(_record(tenant_record_id=OTHER_TENANT))) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_attempt_chronology.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_attempt_chronology.py new file mode 100644 index 000000000..0359dc122 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_attempt_chronology.py @@ -0,0 +1,97 @@ +"""RED contract for immutable verification-attempt release chronology.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityReadPort, + ValidationResultNonVerifiabilityRecord, + resolve_validation_result_nonverifiability, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OWNER_RELEASED_AT = datetime(2026, 9, 17, 5, 45, tzinfo=timezone.utc) +FAILED_EVIDENCE_RELEASED_AT = datetime(2026, 9, 17, 5, 50, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> ValidationResultNonVerifiabilityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": ( + "validation_analysis_result:11111111-1111-4111-8111-111111111111" + ), + "result_digest": "1" * 64, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "non_reproducible", + "failed_evidence_reference": ( + "analysis_weight_receipt:22222222-2222-4222-8222-222222222222" + ), + "failed_evidence_digest": "2" * 64, + "failed_evidence_released_at": FAILED_EVIDENCE_RELEASED_AT, + "verification_attempt_reference": ( + "validation_evidence_verification_attempt:" + "33333333-3333-4333-8333-333333333333" + ), + "verification_attempt_digest": "3" * 64, + "verification_attempt_released_at": ATTEMPT_RELEASED_AT, + "owner_contract_reference": ( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + "owner_contract_version": 7, + "owner_contract_digest": "4" * 64, + "owner_contract_released_at": OWNER_RELEASED_AT, + "evaluated_at": EVALUATED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return ValidationResultNonVerifiabilityRecord(**values) + + +def test_attempt_release_is_owner_evidence_not_lookup_coordinate() -> None: + assert "verification_attempt_released_at" not in signature( + resolve_validation_result_nonverifiability + ).parameters + assert ( + "verification_attempt_released_at" + not in signature( + ValidationResultNonVerifiabilityReadPort.read_validation_result_nonverifiability + ).parameters + ) + + +def test_verification_attempt_release_is_retained() -> None: + record = _record() + assert record.verification_attempt_released_at == ATTEMPT_RELEASED_AT + + +def test_verification_attempt_cannot_be_released_before_evaluation() -> None: + with pytest.raises(ValueError, match="verification_attempt_released_at"): + _record( + verification_attempt_released_at=datetime( + 2026, 9, 17, 5, 59, 59, tzinfo=timezone.utc + ) + ) + + +def test_verification_attempt_must_exist_before_outcome_release() -> None: + with pytest.raises(ValueError, match="verification_attempt_released_at"): + _record( + verification_attempt_released_at=datetime( + 2026, 9, 17, 6, 5, 1, tzinfo=timezone.utc + ) + ) + + +def test_verification_attempt_release_requires_timezone() -> None: + with pytest.raises(ValueError, match="standard-library timezone provider"): + _record(verification_attempt_released_at=datetime(2026, 9, 17, 6, 2)) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_currentness.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_currentness.py new file mode 100644 index 000000000..06d51171b --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_currentness.py @@ -0,0 +1,171 @@ +"""RED contract for owner-resolved non-verifiability currentness.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityIntegrityError, + ValidationResultNonVerifiabilityReadPort, + ValidationResultNonVerifiabilityRecord, + resolve_validation_result_nonverifiability, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +ATTEMPT_REFERENCE = ( + "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +) +OWNER_REFERENCE = "released_owner_contract:44444444-4444-4444-8444-444444444444" +RESULT_DIGEST = "1" * 64 +ATTEMPT_DIGEST = "3" * 64 +OWNER_DIGEST = "4" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) +SUPERSEDED_AT = datetime(2026, 9, 17, 6, 20, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "verification_status", + "failed_evidence_kind", + "failure_mode", + "failed_evidence_reference", + "failed_evidence_digest", + "failed_evidence_released_at", + "verification_attempt_reference", + "verification_attempt_digest", + "verification_attempt_released_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "evaluated_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return one canonical owner record.""" + + def __init__(self, record: ValidationResultNonVerifiabilityRecord) -> None: + self.record = record + + def read_validation_result_nonverifiability( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + failed_evidence_reference: str | None, + failed_evidence_digest: str | None, + verification_attempt_reference: str, + verification_attempt_digest: str, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultNonVerifiabilityRecord: + return self.record + + +def _record(*, superseded_at: datetime | None = SUPERSEDED_AT) -> ValidationResultNonVerifiabilityRecord: + return ValidationResultNonVerifiabilityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + failed_evidence_reference=None, + failed_evidence_digest=None, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + verification_attempt_released_at=ATTEMPT_RELEASED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + evaluated_at=EVALUATED_AT, + released_at=RELEASED_AT, + superseded_at=superseded_at, + ) + + +def _resolve(*, record: ValidationResultNonVerifiabilityRecord, used_at: datetime): + return resolve_validation_result_nonverifiability( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-read-v1", + resource_kind="validation_result_nonverifiability", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(record), + ) + + +def test_nonverifiability_cutover_is_owner_evidence_not_caller_coordinate() -> None: + assert "superseded_at" not in signature(resolve_validation_result_nonverifiability).parameters + assert ( + "superseded_at" + not in signature( + ValidationResultNonVerifiabilityReadPort.read_validation_result_nonverifiability + ).parameters + ) + + +def test_nonverifiability_is_valid_only_before_owner_resolved_cutover() -> None: + record = _record() + + historical = _resolve( + record=record, + used_at=datetime(2026, 9, 17, 6, 19, 59, tzinfo=timezone.utc), + ) + assert dict(historical.fields)["superseded_at"] == SUPERSEDED_AT + + with pytest.raises( + ValidationResultNonVerifiabilityIntegrityError, + match="supersession instant", + ): + _resolve(record=record, used_at=SUPERSEDED_AT) + + +def test_nonverifiability_cutover_must_follow_release_and_be_timezone_aware() -> None: + with pytest.raises(ValueError, match="later than"): + _record(superseded_at=RELEASED_AT) + with pytest.raises(ValueError, match="standard-library timezone provider"): + _record(superseded_at=datetime(2026, 9, 17, 6, 20)) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_failed_evidence_chronology.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_failed_evidence_chronology.py new file mode 100644 index 000000000..a3f6af3d4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_failed_evidence_chronology.py @@ -0,0 +1,100 @@ +"""RED contract for chronology of non-reproducible validation evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityReadPort, + ValidationResultNonVerifiabilityRecord, + resolve_validation_result_nonverifiability, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OWNER_RELEASED_AT = datetime(2026, 9, 17, 5, 45, tzinfo=timezone.utc) +FAILED_EVIDENCE_RELEASED_AT = datetime(2026, 9, 17, 5, 50, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> ValidationResultNonVerifiabilityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": ( + "validation_analysis_result:11111111-1111-4111-8111-111111111111" + ), + "result_digest": "1" * 64, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "non_reproducible", + "failed_evidence_reference": ( + "analysis_weight_receipt:22222222-2222-4222-8222-222222222222" + ), + "failed_evidence_digest": "2" * 64, + "verification_attempt_reference": ( + "validation_evidence_verification_attempt:" + "33333333-3333-4333-8333-333333333333" + ), + "verification_attempt_digest": "3" * 64, + "verification_attempt_released_at": ATTEMPT_RELEASED_AT, + "owner_contract_reference": ( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + "owner_contract_version": 7, + "owner_contract_digest": "4" * 64, + "owner_contract_released_at": OWNER_RELEASED_AT, + "evaluated_at": EVALUATED_AT, + "released_at": RELEASED_AT, + "failed_evidence_released_at": FAILED_EVIDENCE_RELEASED_AT, + } + values.update(overrides) + return ValidationResultNonVerifiabilityRecord(**values) + + +def test_failed_evidence_release_is_owner_evidence_not_lookup_coordinate() -> None: + assert "failed_evidence_released_at" not in signature( + resolve_validation_result_nonverifiability + ).parameters + assert ( + "failed_evidence_released_at" + not in signature( + ValidationResultNonVerifiabilityReadPort.read_validation_result_nonverifiability + ).parameters + ) + + +def test_non_reproducible_evidence_must_exist_before_verification_evaluation() -> None: + with pytest.raises(ValueError, match="failed_evidence_released_at"): + _record( + failed_evidence_released_at=datetime( + 2026, 9, 17, 6, 0, 1, tzinfo=timezone.utc + ) + ) + + +def test_non_reproducible_evidence_requires_release_chronology() -> None: + with pytest.raises(ValueError, match="failed_evidence_released_at"): + _record(failed_evidence_released_at=None) + with pytest.raises(ValueError, match="standard-library timezone provider"): + _record(failed_evidence_released_at=datetime(2026, 9, 17, 5, 50)) + + +def test_missing_evidence_cannot_fabricate_release_chronology() -> None: + with pytest.raises(ValueError, match="must be absent"): + _record( + failure_mode="missing", + failed_evidence_reference=None, + failed_evidence_digest=None, + failed_evidence_released_at=FAILED_EVIDENCE_RELEASED_AT, + ) + + +def test_non_reproducible_release_chronology_is_retained() -> None: + record = _record() + assert record.failed_evidence_released_at == FAILED_EVIDENCE_RELEASED_AT diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_lookup_key_completeness.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_lookup_key_completeness.py new file mode 100644 index 000000000..25b182f7e --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_lookup_key_completeness.py @@ -0,0 +1,67 @@ +"""Fail closed on ambiguous lookup of immutable verification attempts and failed artifacts.""" + +from __future__ import annotations + +from inspect import signature + +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityReadPort, + resolve_validation_result_nonverifiability, +) + + +_REQUIRED_ATTEMPT_COORDINATES = ( + "verification_attempt_reference", + "verification_attempt_digest", +) +_REQUIRED_FAILED_ARTIFACT_COORDINATES = ( + "failed_evidence_reference", + "failed_evidence_digest", +) + + +def test_resolver_requires_exact_verification_attempt_coordinates() -> None: + """Bind callers to one immutable attempt instead of an arbitrary matching outcome.""" + parameters = signature(resolve_validation_result_nonverifiability).parameters + for coordinate in _REQUIRED_ATTEMPT_COORDINATES: + assert coordinate in parameters + + +def test_owner_read_port_keys_exact_verification_attempt_coordinates() -> None: + """Prevent repeated attempts for one result from sharing an ambiguous lookup prefix.""" + parameters = signature( + ValidationResultNonVerifiabilityReadPort.read_validation_result_nonverifiability + ).parameters + for coordinate in _REQUIRED_ATTEMPT_COORDINATES: + assert coordinate in parameters + + +def test_non_reproducible_lookup_requires_exact_failed_artifact_coordinates() -> None: + """Keep same-family failed artifacts distinct before owner resolution.""" + resolver_parameters = signature(resolve_validation_result_nonverifiability).parameters + port_parameters = signature( + ValidationResultNonVerifiabilityReadPort.read_validation_result_nonverifiability + ).parameters + for coordinate in _REQUIRED_FAILED_ARTIFACT_COORDINATES: + assert coordinate in resolver_parameters + assert coordinate in port_parameters + + +def test_attempt_release_time_remains_owner_evidence_not_lookup_authority() -> None: + """Keep release chronology owner-resolved while the immutable attempt identity is exact.""" + resolver_parameters = signature(resolve_validation_result_nonverifiability).parameters + port_parameters = signature( + ValidationResultNonVerifiabilityReadPort.read_validation_result_nonverifiability + ).parameters + assert "verification_attempt_released_at" not in resolver_parameters + assert "verification_attempt_released_at" not in port_parameters + + +def test_failed_artifact_release_time_remains_owner_evidence_not_lookup_authority() -> None: + """Require artifact identity without accepting caller-supplied release chronology.""" + resolver_parameters = signature(resolve_validation_result_nonverifiability).parameters + port_parameters = signature( + ValidationResultNonVerifiabilityReadPort.read_validation_result_nonverifiability + ).parameters + assert "failed_evidence_released_at" not in resolver_parameters + assert "failed_evidence_released_at" not in port_parameters diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_owner_contract_chronology.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_owner_contract_chronology.py new file mode 100644 index 000000000..4ce0683e0 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_owner_contract_chronology.py @@ -0,0 +1,78 @@ +"""Fail closed when non-verifiability owner contracts are retroactive.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityRecord, + resolve_validation_result_nonverifiability, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) + + +def _record(**overrides: object) -> ValidationResultNonVerifiabilityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": ( + "validation_analysis_result:11111111-1111-4111-8111-111111111111" + ), + "result_digest": "1" * 64, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "missing", + "failed_evidence_reference": None, + "failed_evidence_digest": None, + "verification_attempt_reference": ( + "validation_evidence_verification_attempt:" + "33333333-3333-4333-8333-333333333333" + ), + "verification_attempt_digest": "3" * 64, + "verification_attempt_released_at": ATTEMPT_RELEASED_AT, + "owner_contract_reference": ( + "released_owner_contract:44444444-4444-4444-8444-444444444444" + ), + "owner_contract_version": 7, + "owner_contract_digest": "4" * 64, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "evaluated_at": EVALUATED_AT, + "released_at": RELEASED_AT, + } + values.update(overrides) + return ValidationResultNonVerifiabilityRecord(**values) + + +def test_owner_contract_release_is_owner_resolved_not_a_request_coordinate() -> None: + assert "owner_contract_released_at" not in signature( + resolve_validation_result_nonverifiability + ).parameters + + +def test_owner_contract_must_exist_before_verification_evaluation() -> None: + with pytest.raises(ValueError, match="owner_contract_released_at"): + _record( + owner_contract_released_at=datetime( + 2026, 9, 17, 6, 0, 1, tzinfo=timezone.utc + ) + ) + + +def test_owner_contract_release_requires_timezone_aware_evidence() -> None: + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 17, 5, 55)) + + +def test_owner_contract_can_be_released_immediately_before_evaluation() -> None: + record = _record() + assert record.owner_contract_released_at == OWNER_CONTRACT_RELEASED_AT + assert record.evaluated_at == EVALUATED_AT diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_structural_integrity.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_structural_integrity.py new file mode 100644 index 000000000..ba34415e3 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_structural_integrity.py @@ -0,0 +1,143 @@ +"""Regression coverage for canonical non-verifiability owner evidence structure.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityIntegrityError, + ValidationResultNonVerifiabilityRecord, + resolve_validation_result_nonverifiability, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +ATTEMPT_REFERENCE = ( + "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +) +OWNER_REFERENCE = "released_owner_contract:44444444-4444-4444-8444-444444444444" +RESULT_DIGEST = "1" * 64 +ATTEMPT_DIGEST = "3" * 64 +OWNER_DIGEST = "4" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 6, 10, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "verification_status", + "failed_evidence_kind", + "failure_mode", + "failed_evidence_reference", + "failed_evidence_digest", + "failed_evidence_released_at", + "verification_attempt_reference", + "verification_attempt_digest", + "verification_attempt_released_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "evaluated_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured owner evidence without normalizing its tuple structure.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_validation_result_nonverifiability(self, **_: object) -> object: + return self.result + + +def _record() -> ValidationResultNonVerifiabilityRecord: + return ValidationResultNonVerifiabilityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + failed_evidence_reference=None, + failed_evidence_digest=None, + failed_evidence_released_at=None, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + verification_attempt_released_at=ATTEMPT_RELEASED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + evaluated_at=EVALUATED_AT, + released_at=RELEASED_AT, + ) + + +def _resolve(read_port: object) -> object: + principal = ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-read-v1", + resource_kind="validation_result_nonverifiability", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + return resolve_validation_result_nonverifiability( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + failed_evidence_reference=None, + failed_evidence_digest=None, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=read_port, + ) + + +def test_owner_port_cannot_append_hidden_tuple_fields_to_exact_record_type() -> None: + valid = _record() + forged = tuple.__new__( + ValidationResultNonVerifiabilityRecord, + (*tuple(valid), "hidden-unreviewed-owner-coordinate"), + ) + + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve(_ReadPort(forged)) + + +def test_malformed_exact_record_type_maps_to_integrity_error() -> None: + valid = _record() + forged = tuple.__new__(ValidationResultNonVerifiabilityRecord, tuple(valid)[:-1]) + + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _resolve(_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_successor_evidence_binding.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_successor_evidence_binding.py new file mode 100644 index 000000000..6ed3c3d09 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_successor_evidence_binding.py @@ -0,0 +1,65 @@ +"""Bind negative-outcome supersession to the same failed-evidence obligation.""" + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +RESULT_DIGEST = "1" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_ATTEMPT_DIGEST = "4" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 18, 8, tzinfo=timezone.utc) +CUTOVER = RELEASED_AT + timedelta(hours=1) + + +def _record( + successor_failed_evidence_kind: str, +) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Build one supersession edge with an explicit successor evidence obligation.""" + return ValidationResultNonVerifiabilitySupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=CUTOVER, + successor_target_result_reference=RESULT_REFERENCE, + successor_target_result_digest=RESULT_DIGEST, + successor_failed_evidence_kind=successor_failed_evidence_kind, + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_verification_attempt_released_at=CUTOVER, + ) + + +def test_successor_attempt_preserves_the_failed_evidence_obligation() -> None: + """Persist the obligation family that the successor attempt actually re-evaluates.""" + successor = dict(_record("analysis_weight_receipt").successor_fields or ()) + assert successor["successor_failed_evidence_kind"] == "analysis_weight_receipt" + + +def test_unrelated_evidence_family_cannot_retire_the_negative_outcome() -> None: + """A same-result attempt for another evidence family cannot end this failure interval.""" + with pytest.raises(ValueError, match="same failed-evidence obligation"): + _record("variance_design_receipt") diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_successor_result_binding.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_successor_result_binding.py new file mode 100644 index 000000000..03862df55 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_successor_result_binding.py @@ -0,0 +1,88 @@ +"""Bind negative-outcome supersession to a re-verification of the same result.""" + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OTHER_RESULT_REFERENCE = "validation_analysis_result:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +RESULT_DIGEST = "1" * 64 +OTHER_RESULT_DIGEST = "a" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_ATTEMPT_DIGEST = "4" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 18, 8, tzinfo=timezone.utc) +CUTOVER = RELEASED_AT + timedelta(hours=1) + + +def _record( + *, + successor_target_result_reference: str | None = RESULT_REFERENCE, + successor_target_result_digest: str | None = RESULT_DIGEST, +) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Build a released predecessor with one owner-resolved successor attempt.""" + return ValidationResultNonVerifiabilitySupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=CUTOVER, + successor_target_result_reference=successor_target_result_reference, + successor_target_result_digest=successor_target_result_digest, + successor_failed_evidence_kind="analysis_weight_receipt", + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_verification_attempt_released_at=CUTOVER, + ) + + +def test_successor_attempt_is_bound_to_the_exact_predecessor_result() -> None: + """Persist the successor target while keeping it owner-internal and exact.""" + record = _record() + + successor = dict(record.successor_fields or ()) + assert successor["successor_target_result_reference"] == RESULT_REFERENCE + assert successor["successor_target_result_digest"] == RESULT_DIGEST + assert successor["successor_failed_evidence_kind"] == "analysis_weight_receipt" + + +@pytest.mark.parametrize( + ("successor_target_result_reference", "successor_target_result_digest"), + [ + (OTHER_RESULT_REFERENCE, RESULT_DIGEST), + (RESULT_REFERENCE, OTHER_RESULT_DIGEST), + (None, RESULT_DIGEST), + (RESULT_REFERENCE, None), + ], +) +def test_unrelated_or_incomplete_successor_result_binding_fails_closed( + successor_target_result_reference: str | None, + successor_target_result_digest: str | None, +) -> None: + """An unrelated verification attempt cannot retire this result's negative outcome.""" + with pytest.raises(ValueError): + _record( + successor_target_result_reference=successor_target_result_reference, + successor_target_result_digest=successor_target_result_digest, + ) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority.py new file mode 100644 index 000000000..c403df64b --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority.py @@ -0,0 +1,224 @@ +"""Require an immutable successor verification attempt behind negative-outcome cutover.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + resolve_validation_result_nonverifiability_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +RESULT_DIGEST = "1" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_ATTEMPT_DIGEST = "4" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 18, 8, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 18, 10, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "verification_attempt_reference", + "verification_attempt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_target_result_reference", + "successor_target_result_digest", + "successor_failed_evidence_kind", + "successor_verification_attempt_reference", + "successor_verification_attempt_digest", + "successor_verification_attempt_released_at", + } +) + + +class _ReadPort: + """Return one configured owner record through the negative-outcome successor shape.""" + + def __init__(self, record: ValidationResultNonVerifiabilitySupersessionAuthorityRecord) -> None: + self.record = record + + def read_validation_result_nonverifiability_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + result_reference: str, + result_digest: str, + failed_evidence_kind: str, + failure_mode: str, + verification_attempt_reference: str, + verification_attempt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Return owner evidence; the resolver verifies every caller-known coordinate.""" + return self.record + + +def _principal() -> ValidationPrincipal: + """Return the canonical tenant-scoped workforce-validation principal.""" + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy() -> PurposeBoundAccessPolicy: + """Return the exact purpose-bound policy for negative-outcome successor evidence.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-supersession-read-v1", + resource_kind="validation_result_nonverifiability_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record( + *, + superseded_at: datetime | None, + successor_reference: str | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Build one canonical predecessor and optional same-obligation successor attempt.""" + has_successor = successor_reference is not None + return ValidationResultNonVerifiabilitySupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=superseded_at, + successor_target_result_reference=(RESULT_REFERENCE if has_successor else None), + successor_target_result_digest=(RESULT_DIGEST if has_successor else None), + successor_failed_evidence_kind=("analysis_weight_receipt" if has_successor else None), + successor_verification_attempt_reference=successor_reference, + successor_verification_attempt_digest=successor_digest, + successor_verification_attempt_released_at=successor_released_at, + ) + + +def _resolve( + record: ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + *, + used_at: datetime = USED_AT, +): + """Resolve the canonical predecessor through its purpose-bound owner port.""" + return resolve_validation_result_nonverifiability_supersession_authority( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=_ReadPort(record), + ) + + +def test_historical_negative_outcome_hides_successor_attempt() -> None: + """Keep historical negative evidence usable without leaking successor coordinates.""" + cutover = USED_AT + timedelta(hours=1) + view = _resolve( + _record( + superseded_at=cutover, + successor_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_released_at=cutover, + ) + ) + + fields = dict(view.fields) + assert fields["verification_attempt_reference"] == ATTEMPT_REFERENCE + assert fields["failed_evidence_kind"] == "analysis_weight_receipt" + assert "superseded_at" not in fields + assert "successor_verification_attempt_reference" not in fields + assert "successor_target_result_reference" not in fields + assert "successor_failed_evidence_kind" not in fields + + +def test_negative_outcome_fails_closed_at_successor_cutover() -> None: + """Reject use at the exact instant the successor verification attempt takes authority.""" + record = _record( + superseded_at=USED_AT, + successor_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_released_at=USED_AT, + ) + + with pytest.raises(ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError): + _resolve(record) + + +@pytest.mark.parametrize( + ("superseded_at", "successor_reference", "successor_digest", "successor_released_at"), + [ + (USED_AT, None, SUCCESSOR_ATTEMPT_DIGEST, USED_AT), + (None, SUCCESSOR_ATTEMPT_REFERENCE, SUCCESSOR_ATTEMPT_DIGEST, USED_AT), + (USED_AT, ATTEMPT_REFERENCE, SUCCESSOR_ATTEMPT_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_ATTEMPT_REFERENCE, ATTEMPT_DIGEST, USED_AT), + (USED_AT, SUCCESSOR_ATTEMPT_REFERENCE, SUCCESSOR_ATTEMPT_DIGEST, USED_AT - timedelta(seconds=1)), + (USED_AT, SUCCESSOR_ATTEMPT_REFERENCE, SUCCESSOR_ATTEMPT_DIGEST, USED_AT + timedelta(seconds=1)), + (RELEASED_AT, SUCCESSOR_ATTEMPT_REFERENCE, SUCCESSOR_ATTEMPT_DIGEST, RELEASED_AT), + ], +) +def test_owner_record_rejects_incomplete_or_non_atomic_successor_attempt( + superseded_at: datetime | None, + successor_reference: str | None, + successor_digest: str | None, + successor_released_at: datetime | None, +) -> None: + """Require one complete, new, release-at-cutover successor verification attempt.""" + with pytest.raises(ValueError): + _record( + superseded_at=superseded_at, + successor_reference=successor_reference, + successor_digest=successor_digest, + successor_released_at=successor_released_at, + ) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_edges.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_edges.py new file mode 100644 index 000000000..a99afeeab --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_edges.py @@ -0,0 +1,386 @@ +"""Hostile edges for append-only non-verifiability successor authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionAuthorityNotFound, + ValidationResultNonVerifiabilitySupersessionAuthorityReadPort, + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + ValidationResultNonVerifiabilitySupersessionAuthorityView, + resolve_validation_result_nonverifiability_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OTHER_RESULT_REFERENCE = "validation_analysis_result:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +OTHER_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:55555555-5555-4555-8555-555555555555" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +OTHER_OWNER_CONTRACT_REFERENCE = "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +RESULT_DIGEST = "1" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_ATTEMPT_DIGEST = "5" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +RELEASED_AT = datetime(2026, 9, 18, 8, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 18, 10, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "verification_attempt_reference", + "verification_attempt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_target_result_reference", + "successor_target_result_digest", + "successor_failed_evidence_kind", + "successor_verification_attempt_reference", + "successor_verification_attempt_digest", + "successor_verification_attempt_released_at", + } +) + + +class _ReadPort: + """Return configured authority while retaining exact lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_validation_result_nonverifiability_supersession_authority( + self, **coordinates: object + ) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(ValidationResultNonVerifiabilitySupersessionAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that static capability validation must reject.""" + + @property + def read_validation_result_nonverifiability_supersession_authority(self) -> object: + """Fail if descriptor execution leaks through static capability validation.""" + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + """Return a tenant-scoped validation principal.""" + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + """Return the exact purpose-bound policy for successor evidence.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-supersession-read-v1", + resource_kind="validation_result_nonverifiability_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Build canonical owner evidence with optional hostile overrides.""" + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "missing", + "verification_attempt_reference": ATTEMPT_REFERENCE, + "verification_attempt_digest": ATTEMPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + "successor_target_result_reference": None, + "successor_target_result_digest": None, + "successor_failed_evidence_kind": None, + "successor_verification_attempt_reference": None, + "successor_verification_attempt_digest": None, + "successor_verification_attempt_released_at": None, + } + values.update(overrides) + if ( + "successor_target_result_reference" not in overrides + and values["successor_verification_attempt_reference"] is not None + ): + values["successor_target_result_reference"] = values["result_reference"] + if ( + "successor_target_result_digest" not in overrides + and values["successor_verification_attempt_reference"] is not None + ): + values["successor_target_result_digest"] = values["result_digest"] + if ( + "successor_failed_evidence_kind" not in overrides + and values["successor_verification_attempt_reference"] is not None + ): + values["successor_failed_evidence_kind"] = values["failed_evidence_kind"] + return ValidationResultNonVerifiabilitySupersessionAuthorityRecord(**values) + + +def _resolve( + *, read_port: object, **overrides: object +) -> ValidationResultNonVerifiabilitySupersessionAuthorityView: + """Resolve canonical request coordinates with optional hostile overrides.""" + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "missing", + "verification_attempt_reference": ATTEMPT_REFERENCE, + "verification_attempt_digest": ATTEMPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_validation_result_nonverifiability_supersession_authority(**values) + + +def test_current_negative_outcome_resolves_without_successor_coordinates() -> None: + """Use owner chronology while keeping successor and cutover data private.""" + port = _ReadPort(_record()) + view = _resolve(read_port=port) + + assert isinstance(port, ValidationResultNonVerifiabilitySupersessionAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["verification_attempt_reference"] == ATTEMPT_REFERENCE + assert "owner_contract_released_at" not in port.calls[0] + assert "released_at" not in port.calls[0] + assert "superseded_at" not in port.calls[0] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["owner_contract_released_at"] == OWNER_CONTRACT_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert "superseded_at" not in fields + assert "successor_verification_attempt_reference" not in fields + assert "successor_target_result_reference" not in fields + assert "successor_failed_evidence_kind" not in fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + """Keep denial ahead of any owner evidence lookup.""" + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + """Reject absence and foreign record types after authorization.""" + with pytest.raises(ValidationResultNonVerifiabilitySupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"result_reference": OTHER_RESULT_REFERENCE}, + {"result_digest": "a" * 64}, + {"failed_evidence_kind": "variance_design_receipt"}, + {"failure_mode": "non_reproducible"}, + {"verification_attempt_reference": OTHER_ATTEMPT_REFERENCE}, + {"verification_attempt_digest": "c" * 64}, + {"owner_contract_reference": OTHER_OWNER_CONTRACT_REFERENCE}, + {"owner_contract_version": 4}, + {"owner_contract_digest": "b" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + """Reject owner evidence selected by an incomplete or different lookup tuple.""" + if record_overrides.get("failure_mode") == "non_reproducible": + record_overrides = {"failure_mode": "non_reproducible"} + with pytest.raises((ValueError, ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError)): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_release_chronology_and_historical_use_are_half_open() -> None: + """Reject pre-release use while preserving reproducible historical reads.""" + with pytest.raises(ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + cutover = USED_AT + timedelta(seconds=1) + record = _record( + superseded_at=cutover, + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_verification_attempt_released_at=cutover, + ) + view = _resolve(read_port=_ReadPort(record)) + assert dict(view.fields)["result_digest"] == RESULT_DIGEST + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("result_reference", "wrong:result", ValueError), + ("result_digest", "ABC", ValueError), + ("failed_evidence_kind", "unknown", ValueError), + ("failure_mode", "green", ValueError), + ("verification_attempt_reference", "wrong:attempt", ValueError), + ("verification_attempt_digest", "3" * 63, ValueError), + ("evidence_version", False, ValueError), + ("evidence_version", 2, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "2" * 63, ValueError), + ("used_at", datetime(2026, 9, 18), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + """Validate request and dependency shapes before any owner read occurs.""" + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_rejects_non_v1_and_public_view_construction() -> None: + """Keep evidence version governed and view issuance resolver-only.""" + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(TypeError, match="issued only by"): + ValidationResultNonVerifiabilitySupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + with pytest.raises(ValueError, match="digests must be distinct"): + _record(owner_contract_digest=RESULT_DIGEST) + + +def test_record_rejects_naive_chronology_and_malformed_successor() -> None: + """Reject malformed owner chronology before it can become current evidence.""" + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 1)) + with pytest.raises(ValueError): + _record(released_at=datetime(2026, 9, 18, 8)) + with pytest.raises(ValueError): + _record(owner_contract_released_at=RELEASED_AT + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record( + superseded_at=datetime(2026, 9, 18, 11), + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_verification_attempt_released_at=USED_AT, + ) + with pytest.raises(ValueError): + _record( + superseded_at=USED_AT + timedelta(hours=1), + successor_verification_attempt_reference="analysis_weight_receipt:55555555-5555-4555-8555-555555555555", + successor_verification_attempt_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_verification_attempt_released_at=USED_AT + timedelta(hours=1), + ) + with pytest.raises(ValueError): + _record( + superseded_at=USED_AT + timedelta(hours=1), + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest="not-a-digest", + successor_verification_attempt_released_at=USED_AT + timedelta(hours=1), + ) + + +@pytest.mark.parametrize("alias_digest", [RESULT_DIGEST, ATTEMPT_DIGEST, OWNER_CONTRACT_DIGEST]) +def test_successor_digest_must_not_alias_existing_evidence(alias_digest: str) -> None: + """Require the successor attempt to identify genuinely new immutable evidence.""" + cutover = USED_AT + timedelta(hours=1) + with pytest.raises(ValueError, match="must identify new evidence"): + _record( + superseded_at=cutover, + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest=alias_digest, + successor_verification_attempt_released_at=cutover, + ) + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + """Prevent retained UUID aliases or attribute writes from mutating accepted authority.""" + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + assert record.successor_fields is None + assert dict(record.fields)["evidence_version"] == 1 + with pytest.raises(AttributeError): + object.__setattr__(record, "evidence_version", 2) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_structural_integrity.py new file mode 100644 index 000000000..0ee3951bb --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_structural_integrity.py @@ -0,0 +1,167 @@ +"""Reject non-canonical exact-typed v1 non-verifiability supersession evidence.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + resolve_validation_result_nonverifiability_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +RESULT_DIGEST = "1" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_ATTEMPT_DIGEST = "4" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 18, 8, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 18, 10, tzinfo=timezone.utc) +CUTOVER = USED_AT + timedelta(hours=1) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "verification_attempt_reference", + "verification_attempt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_target_result_reference", + "successor_target_result_digest", + "successor_failed_evidence_kind", + "successor_verification_attempt_reference", + "successor_verification_attempt_digest", + "successor_verification_attempt_released_at", + } +) + + +class _ReadPort: + """Return one configured object so resolver integrity owns the trust decision.""" + + def __init__(self, record: object) -> None: + self.record = record + + def read_validation_result_nonverifiability_supersession_authority( + self, **_: object + ) -> object: + """Return the configured owner evidence without normalizing its structure.""" + return self.record + + +def _record() -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Build one canonical predecessor with a later same-obligation successor.""" + return ValidationResultNonVerifiabilitySupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=CUTOVER, + successor_target_result_reference=RESULT_REFERENCE, + successor_target_result_digest=RESULT_DIGEST, + successor_failed_evidence_kind="analysis_weight_receipt", + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest=SUCCESSOR_ATTEMPT_DIGEST, + successor_verification_attempt_released_at=CUTOVER, + ) + + +def _resolve(record: object) -> None: + """Resolve through the public owner boundary at a historical use instant.""" + resolve_validation_result_nonverifiability_supersession_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="missing", + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-supersession-read-v1", + resource_kind="validation_result_nonverifiability_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(record), + ) + + +def test_v1_supersession_rejects_hidden_outer_tuple_member() -> None: + """Do not normalize away an exact-typed hidden coordinate after owner read.""" + canonical = _record() + forged = tuple.__new__( + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_v1_supersession_maps_truncated_tuple_to_integrity_error() -> None: + """Keep malformed exact-typed evidence inside the family integrity boundary.""" + canonical = _record() + forged = tuple.__new__( + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + tuple(canonical)[:5], + ) + + with pytest.raises(ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_v1_supersession_rejects_duplicate_nested_current_field() -> None: + """Reject duplicate nested coordinates that dict conversion would erase.""" + canonical = _record() + forged_items = list(canonical) + forged_items[2] = canonical.fields + (("result_reference", RESULT_REFERENCE),) + forged = tuple.__new__( + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, + tuple(forged_items), + ) + + with pytest.raises(ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..867325b2e --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for non-verifiability supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_low_level_tuple_construction_cannot_issue_nonverifiability_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + ValidationResultNonVerifiabilitySupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("result_digest", "0" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_nonverifiability_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(ValidationResultNonVerifiabilitySupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + match="was not issued by resolve_validation_result_nonverifiability_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_nonverifiability_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(ValidationResultNonVerifiabilitySupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + match="was not issued by resolve_validation_result_nonverifiability_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_nonverifiability_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(ValidationResultNonVerifiabilitySupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..0aac2f43d --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_authority_view_seal_capability.py @@ -0,0 +1,58 @@ +"""Hostile sealing-capability regression for non-verifiability supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority as authority_module +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000429") +STUDY = UUID("00000000-0000-0000-0000-000000000430") + + +def _raw_view_with_module_marker() -> ValidationResultNonVerifiabilitySupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(ValidationResultNonVerifiabilitySupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__( + view, + "_fields", + (("result_digest", "b" * 64),), + ) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_VALIDATION_RESULT_NONVERIFIABILITY_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_nonverifiability_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_VALIDATION_RESULT_NONVERIFIABILITY_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_nonverifiability_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + ValidationResultNonVerifiabilitySupersessionAuthorityIntegrityError, + match=( + "validation result non-verifiability supersession view was not issued by " + "resolve_validation_result_nonverifiability_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_failed_evidence_binding.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_failed_evidence_binding.py new file mode 100644 index 000000000..18e68677a --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_failed_evidence_binding.py @@ -0,0 +1,56 @@ +"""Fail closed when supersession would discard non-reproducible evidence identity.""" + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_authority import ( + ValidationResultNonVerifiabilitySupersessionAuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RESULT_DIGEST = "1" * 64 +ATTEMPT_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 18, 8, tzinfo=timezone.utc) + + +def _record(*, failure_mode: str) -> ValidationResultNonVerifiabilitySupersessionAuthorityRecord: + """Build one predecessor using the v1 supersession identity currently persisted.""" + return ValidationResultNonVerifiabilitySupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode=failure_mode, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def test_missing_predecessor_remains_supported_without_fabricated_evidence_identity() -> None: + """Keep missing-evidence outcomes representable because no artifact identity exists.""" + fields = dict(_record(failure_mode="missing").fields) + assert fields["failure_mode"] == "missing" + + +def test_nonreproducible_predecessor_fails_closed_when_artifact_identity_would_be_lost() -> None: + """Reject v1 supersession until exact failed reference, digest, and release can be bound.""" + with pytest.raises( + ValueError, + match="non_reproducible supersession requires exact failed-evidence identity", + ): + _record(failure_mode="non_reproducible") diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_edges.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_edges.py new file mode 100644 index 000000000..77df81a38 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_edges.py @@ -0,0 +1,567 @@ +"""Exact-artifact correction contract for non-reproducible validation evidence.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID +from types import SimpleNamespace + +import pytest + +import orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority as v2_module + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.registry import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityRecord, +) +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionV2AuthorityNotFound, + ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort, + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord, + ValidationResultNonVerifiabilitySupersessionV2AuthorityView, + resolve_validation_result_nonverifiability_supersession_v2_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +OTHER_RESULT_REFERENCE = "validation_analysis_result:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +FAILED_REFERENCE = "analysis_weight_receipt:22222222-2222-4222-8222-222222222222" +OTHER_FAILED_REFERENCE = "analysis_weight_receipt:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +OTHER_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:cccccccc-cccc-4ccc-8ccc-cccccccccccc" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +OTHER_OWNER_REFERENCE = "released_owner_contract:dddddddd-dddd-4ddd-8ddd-dddddddddddd" +RESULT_DIGEST = "1" * 64 +FAILED_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_DIGEST = "4" * 64 +OWNER_DIGEST = "5" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +FAILED_RELEASED_AT = datetime(2026, 9, 17, 5, 59, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 6, 10, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "result_reference", + "result_digest", + "failed_evidence_kind", + "failure_mode", + "failed_evidence_reference", + "failed_evidence_digest", + "failed_evidence_released_at", + "verification_attempt_reference", + "verification_attempt_digest", + "verification_attempt_released_at", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_target_result_reference", + "successor_target_result_digest", + "successor_failed_evidence_kind", + "successor_target_failed_evidence_reference", + "successor_target_failed_evidence_digest", + "successor_target_failed_evidence_released_at", + "successor_verification_attempt_reference", + "successor_verification_attempt_digest", + "successor_verification_attempt_released_at", + } +) + + +class _ReadPort: + """Return configured v2 authority and retain caller-known lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_validation_result_nonverifiability_supersession_v2_authority( + self, **coordinates: object + ) -> object: + """Capture the lookup before returning configured owner evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately omit the owner capability.""" + + +class _ProtocolOnly(ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort): + """Inherit only the Protocol placeholder.""" + + +class _DescriptorReadPort: + """Expose a descriptor that static capability validation must reject.""" + + @property + def read_validation_result_nonverifiability_supersession_v2_authority(self) -> object: + """Fail if descriptor execution leaks through static validation.""" + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + """Return the canonical workforce-validation principal.""" + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + """Return the purpose-bound v2 correction policy.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-supersession-read-v2", + resource_kind="validation_result_nonverifiability_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _predecessor(**overrides: object) -> ValidationResultNonVerifiabilityRecord: + """Build one released non-reproducible predecessor outcome.""" + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "failed_evidence_kind": "analysis_weight_receipt", + "failure_mode": "non_reproducible", + "failed_evidence_reference": FAILED_REFERENCE, + "failed_evidence_digest": FAILED_DIGEST, + "failed_evidence_released_at": FAILED_RELEASED_AT, + "verification_attempt_reference": ATTEMPT_REFERENCE, + "verification_attempt_digest": ATTEMPT_DIGEST, + "verification_attempt_released_at": ATTEMPT_RELEASED_AT, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "evaluated_at": EVALUATED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return ValidationResultNonVerifiabilityRecord(**values) + + +def _record( + *, + predecessor: ValidationResultNonVerifiabilityRecord | None = None, + **overrides: object, +) -> ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord: + """Build v2 authority while keeping the ordinary cutover and explicit edge aligned.""" + values: dict[str, object] = { + "evidence_version": 2, + "superseded_at": None, + "successor_target_result_reference": None, + "successor_target_result_digest": None, + "successor_failed_evidence_kind": None, + "successor_target_failed_evidence_reference": None, + "successor_target_failed_evidence_digest": None, + "successor_target_failed_evidence_released_at": None, + "successor_verification_attempt_reference": None, + "successor_verification_attempt_digest": None, + "successor_verification_attempt_released_at": None, + } + values.update(overrides) + if predecessor is None: + requested_cutover = values["superseded_at"] + if ( + type(requested_cutover) is datetime + and requested_cutover.tzinfo is not None + and requested_cutover.utcoffset() is not None + and requested_cutover > RELEASED_AT + ): + predecessor = _predecessor(superseded_at=requested_cutover) + else: + predecessor = _predecessor() + values["predecessor"] = predecessor + return ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord(**values) + + +def _successor_overrides(**overrides: object) -> dict[str, object]: + """Return the complete exact-artifact successor tuple with optional hostile changes.""" + values: dict[str, object] = { + "superseded_at": CUTOVER, + "successor_target_result_reference": RESULT_REFERENCE, + "successor_target_result_digest": RESULT_DIGEST, + "successor_failed_evidence_kind": "analysis_weight_receipt", + "successor_target_failed_evidence_reference": FAILED_REFERENCE, + "successor_target_failed_evidence_digest": FAILED_DIGEST, + "successor_target_failed_evidence_released_at": FAILED_RELEASED_AT, + "successor_verification_attempt_reference": SUCCESSOR_ATTEMPT_REFERENCE, + "successor_verification_attempt_digest": SUCCESSOR_DIGEST, + "successor_verification_attempt_released_at": CUTOVER, + } + values.update(overrides) + return values + + +def _resolve( + *, read_port: object, **overrides: object +) -> ValidationResultNonVerifiabilitySupersessionV2AuthorityView: + """Resolve canonical caller-known coordinates with optional hostile overrides.""" + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "failed_evidence_kind": "analysis_weight_receipt", + "failed_evidence_reference": FAILED_REFERENCE, + "failed_evidence_digest": FAILED_DIGEST, + "verification_attempt_reference": ATTEMPT_REFERENCE, + "verification_attempt_digest": ATTEMPT_DIGEST, + "evidence_version": 2, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_validation_result_nonverifiability_supersession_v2_authority(**values) + + +def test_current_non_reproducible_outcome_resolves_with_exact_failed_artifact() -> None: + """Expose predecessor provenance while keeping correction coordinates private.""" + record = _record() + port = _ReadPort(record) + view = _resolve(read_port=port) + + assert isinstance(port, ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort) + assert record.predecessor.failure_mode == "non_reproducible" + assert record.evidence_version == 2 + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + assert record.successor_fields is None + assert port.calls == [ + { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "result_reference": RESULT_REFERENCE, + "result_digest": RESULT_DIGEST, + "failed_evidence_kind": "analysis_weight_receipt", + "failed_evidence_reference": FAILED_REFERENCE, + "failed_evidence_digest": FAILED_DIGEST, + "verification_attempt_reference": ATTEMPT_REFERENCE, + "verification_attempt_digest": ATTEMPT_DIGEST, + "evidence_version": 2, + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 7, + "owner_contract_digest": OWNER_DIGEST, + } + ] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["failure_mode"] == "non_reproducible" + assert fields["failed_evidence_reference"] == FAILED_REFERENCE + assert fields["failed_evidence_digest"] == FAILED_DIGEST + assert fields["failed_evidence_released_at"] == FAILED_RELEASED_AT + assert fields["verification_attempt_released_at"] == ATTEMPT_RELEASED_AT + assert fields["evidence_version"] == 2 + assert fields["released_at"] == RELEASED_AT + assert "superseded_at" not in fields + assert "successor_target_failed_evidence_reference" not in fields + assert "successor_verification_attempt_reference" not in fields + + +def test_exact_artifact_successor_preserves_historical_use_but_ends_at_cutover() -> None: + """Require one atomic same-result, same-artifact successor attempt.""" + record = _record(**_successor_overrides()) + assert record.predecessor.superseded_at == CUTOVER + assert dict(record.successor_fields or ()) == { + "successor_failed_evidence_kind": "analysis_weight_receipt", + "successor_target_failed_evidence_digest": FAILED_DIGEST, + "successor_target_failed_evidence_reference": FAILED_REFERENCE, + "successor_target_failed_evidence_released_at": FAILED_RELEASED_AT, + "successor_target_result_digest": RESULT_DIGEST, + "successor_target_result_reference": RESULT_REFERENCE, + "successor_verification_attempt_digest": SUCCESSOR_DIGEST, + "successor_verification_attempt_reference": SUCCESSOR_ATTEMPT_REFERENCE, + "successor_verification_attempt_released_at": CUTOVER, + } + view = _resolve(read_port=_ReadPort(record), used_at=CUTOVER - timedelta(seconds=1)) + fields = dict(view.fields) + assert fields["result_reference"] == RESULT_REFERENCE + hidden_owner_coordinates = { + "superseded_at", + "successor_target_result_reference", + "successor_target_result_digest", + "successor_failed_evidence_kind", + "successor_target_failed_evidence_reference", + "successor_target_failed_evidence_digest", + "successor_target_failed_evidence_released_at", + "successor_verification_attempt_reference", + "successor_verification_attempt_digest", + "successor_verification_attempt_released_at", + } + assert hidden_owner_coordinates.isdisjoint(fields) + with pytest.raises(ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError): + _resolve(read_port=_ReadPort(record), used_at=CUTOVER) + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + """Keep purpose denial ahead of owner evidence lookup.""" + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + """Reject absence and foreign record types after authorization.""" + with pytest.raises(ValidationResultNonVerifiabilitySupersessionV2AuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "predecessor_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"result_reference": OTHER_RESULT_REFERENCE}, + {"result_digest": "a" * 64}, + { + "failed_evidence_kind": "variance_design_receipt", + "failed_evidence_reference": ( + "variance_design_receipt:22222222-2222-4222-8222-222222222222" + ), + }, + {"failed_evidence_reference": OTHER_FAILED_REFERENCE}, + {"failed_evidence_digest": "b" * 64}, + {"verification_attempt_reference": OTHER_ATTEMPT_REFERENCE}, + {"verification_attempt_digest": "c" * 64}, + {"owner_contract_reference": OTHER_OWNER_REFERENCE}, + {"owner_contract_version": 8}, + {"owner_contract_digest": "d" * 64}, + ], +) +def test_owner_evidence_must_match_every_caller_known_coordinate( + predecessor_overrides: dict[str, object] +) -> None: + """Reject evidence selected by an incomplete or different lookup tuple.""" + record = _record(predecessor=_predecessor(**predecessor_overrides)) + with pytest.raises(ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError): + _resolve(read_port=_ReadPort(record)) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("result_reference", "wrong:result", ValueError), + ("result_digest", "ABC", ValueError), + ("failed_evidence_kind", "unknown", ValueError), + ("failed_evidence_reference", "wrong:artifact", ValueError), + ("failed_evidence_digest", "2" * 63, ValueError), + ("verification_attempt_reference", "wrong:attempt", ValueError), + ("verification_attempt_digest", "3" * 63, ValueError), + ("evidence_version", False, ValueError), + ("evidence_version", 1, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "5" * 63, ValueError), + ("used_at", datetime(2026, 9, 17, 6, 10), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + """Validate caller/dependency shapes before any owner read.""" + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_pre_release_use_fails_closed() -> None: + """Do not expose a predecessor before its released authority instant.""" + with pytest.raises(ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError): + _resolve( + read_port=_ReadPort(_record()), + used_at=RELEASED_AT - timedelta(seconds=1), + ) + + +def test_record_requires_exact_non_reproducible_predecessor_and_v2() -> None: + """Keep v2 reserved for exact ordinary non-reproducible owner evidence.""" + with pytest.raises(TypeError, match="exact ValidationResultNonVerifiabilityRecord"): + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=object(), evidence_version=2 + ) + with pytest.raises(ValueError, match="reserved for non_reproducible"): + _record( + predecessor=_predecessor( + failure_mode="missing", + failed_evidence_reference=None, + failed_evidence_digest=None, + failed_evidence_released_at=None, + ) + ) + with pytest.raises(ValueError, match="evidence_version must be 2"): + _record(evidence_version=1) + with pytest.raises(TypeError, match="issued only by"): + ValidationResultNonVerifiabilitySupersessionV2AuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_record_rechecks_exact_failed_artifact_after_predecessor_revalidation( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Retain the constructor guard if predecessor revalidation ever regresses.""" + incomplete = SimpleNamespace( + failure_mode="non_reproducible", + failed_evidence_reference=None, + failed_evidence_digest=FAILED_DIGEST, + failed_evidence_released_at=FAILED_RELEASED_AT, + ) + monkeypatch.setattr(v2_module, "_revalidate_predecessor", lambda _value: incomplete) + + with pytest.raises(ValueError, match="retain exact failed-artifact evidence"): + _record() + + +def test_incomplete_successor_tuple_fails_closed() -> None: + """Never accept a cutover without every exact-artifact successor coordinate.""" + values = _successor_overrides() + values["successor_target_failed_evidence_digest"] = None + with pytest.raises(ValueError, match="requires cutover"): + _record(**values) + + +@pytest.mark.parametrize( + ("override", "message"), + [ + ({"superseded_at": RELEASED_AT}, "later than predecessor release"), + ({"successor_target_result_reference": OTHER_RESULT_REFERENCE}, "exact predecessor result"), + ({"successor_target_result_digest": "a" * 64}, "exact predecessor result"), + ({"successor_failed_evidence_kind": "variance_design_receipt"}, "same failed-evidence family"), + ({"successor_target_failed_evidence_reference": OTHER_FAILED_REFERENCE}, "exact failed artifact"), + ({"successor_target_failed_evidence_digest": "b" * 64}, "exact failed artifact"), + ( + {"successor_target_failed_evidence_released_at": FAILED_RELEASED_AT + timedelta(seconds=1)}, + "exact failed artifact", + ), + ({"successor_verification_attempt_reference": ATTEMPT_REFERENCE}, "new reference"), + ({"successor_verification_attempt_digest": RESULT_DIGEST}, "identify new evidence"), + ({"successor_verification_attempt_digest": FAILED_DIGEST}, "identify new evidence"), + ({"successor_verification_attempt_digest": ATTEMPT_DIGEST}, "identify new evidence"), + ({"successor_verification_attempt_digest": OWNER_DIGEST}, "identify new evidence"), + ( + {"successor_verification_attempt_released_at": CUTOVER + timedelta(seconds=1)}, + "released exactly at supersession", + ), + ], +) +def test_successor_must_bind_exact_predecessor_artifact( + override: dict[str, object], message: str +) -> None: + """Reject another result, artifact, family, reused attempt, alias, or split cutover.""" + with pytest.raises(ValueError, match=message): + _record(**_successor_overrides(**override)) + + +@pytest.mark.parametrize( + "override", + [ + {"superseded_at": datetime(2026, 9, 17, 7, 0)}, + {"successor_target_result_reference": "wrong:result"}, + {"successor_target_result_digest": "x"}, + {"successor_failed_evidence_kind": "unknown"}, + {"successor_target_failed_evidence_reference": "wrong:artifact"}, + {"successor_target_failed_evidence_digest": "x"}, + {"successor_target_failed_evidence_released_at": datetime(2026, 9, 17, 5, 59)}, + {"successor_verification_attempt_reference": "wrong:attempt"}, + {"successor_verification_attempt_digest": "x"}, + {"successor_verification_attempt_released_at": datetime(2026, 9, 17, 7, 0)}, + ], +) +def test_malformed_successor_coordinates_fail_closed( + override: dict[str, object] +) -> None: + """Reject malformed references, digests and naive chronology before correction use.""" + with pytest.raises(ValueError): + _record(**_successor_overrides(**override)) + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + """Prevent retained aliases or attribute writes from mutating accepted authority.""" + tenant = UUID(str(TENANT)) + predecessor = _predecessor(tenant_record_id=tenant) + record = _record(predecessor=predecessor) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.predecessor.tenant_record_id == TENANT + assert dict(record.fields)["failed_evidence_reference"] == FAILED_REFERENCE + with pytest.raises(AttributeError): + object.__setattr__(record, "evidence_version", 3) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + + +def test_v2_rejects_structurally_forged_non_reproducible_predecessor() -> None: + """Revalidate exact failed-artifact presence even if tuple construction bypasses v1 guards.""" + predecessor = _predecessor() + forged_values = list(predecessor) + forged_values[6] = None + forged = tuple.__new__(ValidationResultNonVerifiabilityRecord, forged_values) + with pytest.raises(ValueError, match="failed evidence reference and digest"): + _record(predecessor=forged) + + +def test_owner_record_rejects_hidden_v2_structure() -> None: + """Reject hidden tuple coordinates even when visible owner evidence is valid.""" + record = _record() + forged = tuple.__new__( + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord, + tuple(record) + ("hidden-coordinate",), + ) + + with pytest.raises( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + match="non-canonical", + ): + _resolve(read_port=_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_view_issuance_integrity.py new file mode 100644 index 000000000..afc040eb3 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for v2 non-verifiability supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionV2AuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_low_level_tuple_construction_cannot_issue_v2_nonverifiability_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + ValidationResultNonVerifiabilitySupersessionV2AuthorityView, + ( + TENANT.int, + STUDY.int, + (("result_digest", "0" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_v2_nonverifiability_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(ValidationResultNonVerifiabilitySupersessionV2AuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + match="was not issued by resolve_validation_result_nonverifiability_supersession_v2_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_v2_nonverifiability_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(ValidationResultNonVerifiabilitySupersessionV2AuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + match="was not issued by resolve_validation_result_nonverifiability_supersession_v2_authority", + ): + _ = forged_view.fields + + +def test_raw_v2_nonverifiability_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(ValidationResultNonVerifiabilitySupersessionV2AuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_view_seal_capability.py new file mode 100644 index 000000000..e5d4fda7a --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_authority_view_seal_capability.py @@ -0,0 +1,54 @@ +"""Hostile sealing-capability regression for v2 non-verifiability supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority as authority_module +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionV2AuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000431") +STUDY = UUID("00000000-0000-0000-0000-000000000432") + + +def _raw_view_with_module_marker() -> ValidationResultNonVerifiabilitySupersessionV2AuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(ValidationResultNonVerifiabilitySupersessionV2AuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__(view, "_fields", (("result_digest", "b" * 64),)) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_VALIDATION_RESULT_NONVERIFIABILITY_SUPERSESSION_V2_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_nonverifiability_supersession_v2_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_VALIDATION_RESULT_NONVERIFIABILITY_SUPERSESSION_V2_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_nonverifiability_supersession_v2_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + match=( + "validation result non-verifiability supersession v2 view was not issued by " + "resolve_validation_result_nonverifiability_supersession_v2_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_contract.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_contract.py new file mode 100644 index 000000000..c7fbb6adc --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_contract.py @@ -0,0 +1,78 @@ +"""RED contract for exact-artifact supersession of non-reproducible outcomes.""" + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityRecord, +) +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +FAILED_REFERENCE = "analysis_weight_receipt:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +RESULT_DIGEST = "1" * 64 +FAILED_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_DIGEST = "4" * 64 +OWNER_DIGEST = "5" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +FAILED_RELEASED_AT = datetime(2026, 9, 17, 5, 59, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc) + + +def _predecessor() -> ValidationResultNonVerifiabilityRecord: + return ValidationResultNonVerifiabilityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="non_reproducible", + failed_evidence_reference=FAILED_REFERENCE, + failed_evidence_digest=FAILED_DIGEST, + failed_evidence_released_at=FAILED_RELEASED_AT, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + verification_attempt_released_at=ATTEMPT_RELEASED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + evaluated_at=EVALUATED_AT, + released_at=RELEASED_AT, + superseded_at=CUTOVER, + ) + + +def test_non_reproducible_correction_binds_the_exact_failed_artifact() -> None: + """A different artifact in the same evidence family must not retire the predecessor.""" + predecessor = _predecessor() + with pytest.raises(ValueError, match="exact failed artifact"): + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=predecessor, + evidence_version=2, + superseded_at=CUTOVER, + successor_target_result_reference=RESULT_REFERENCE, + successor_target_result_digest=RESULT_DIGEST, + successor_failed_evidence_kind="analysis_weight_receipt", + successor_target_failed_evidence_reference=( + "analysis_weight_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" + ), + successor_target_failed_evidence_digest=FAILED_DIGEST, + successor_target_failed_evidence_released_at=FAILED_RELEASED_AT, + successor_verification_attempt_reference=SUCCESSOR_ATTEMPT_REFERENCE, + successor_verification_attempt_digest=SUCCESSOR_DIGEST, + successor_verification_attempt_released_at=CUTOVER, + ) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_cutover_alignment.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_cutover_alignment.py new file mode 100644 index 000000000..7193993d6 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_cutover_alignment.py @@ -0,0 +1,101 @@ +"""RED contract binding v2 correction edges to ordinary predecessor cutover chronology.""" + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityRecord, +) +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +FAILED_REFERENCE = "analysis_weight_receipt:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +SUCCESSOR_ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +RESULT_DIGEST = "1" * 64 +FAILED_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +SUCCESSOR_DIGEST = "4" * 64 +OWNER_DIGEST = "5" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +FAILED_RELEASED_AT = datetime(2026, 9, 17, 5, 59, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 17, 7, 0, tzinfo=timezone.utc) +OTHER_CUTOVER = datetime(2026, 9, 17, 7, 1, tzinfo=timezone.utc) + + +def _predecessor(*, superseded_at: datetime | None) -> ValidationResultNonVerifiabilityRecord: + return ValidationResultNonVerifiabilityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="non_reproducible", + failed_evidence_reference=FAILED_REFERENCE, + failed_evidence_digest=FAILED_DIGEST, + failed_evidence_released_at=FAILED_RELEASED_AT, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + verification_attempt_released_at=ATTEMPT_RELEASED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + evaluated_at=EVALUATED_AT, + released_at=RELEASED_AT, + superseded_at=superseded_at, + ) + + +def _successor_values(*, superseded_at: datetime) -> dict[str, object]: + return { + "superseded_at": superseded_at, + "successor_target_result_reference": RESULT_REFERENCE, + "successor_target_result_digest": RESULT_DIGEST, + "successor_failed_evidence_kind": "analysis_weight_receipt", + "successor_target_failed_evidence_reference": FAILED_REFERENCE, + "successor_target_failed_evidence_digest": FAILED_DIGEST, + "successor_target_failed_evidence_released_at": FAILED_RELEASED_AT, + "successor_verification_attempt_reference": SUCCESSOR_ATTEMPT_REFERENCE, + "successor_verification_attempt_digest": SUCCESSOR_DIGEST, + "successor_verification_attempt_released_at": superseded_at, + } + + +def test_successor_requires_the_same_cutover_as_the_ordinary_predecessor() -> None: + """An explicit edge must not disagree with the ordinary owner projection.""" + with pytest.raises(ValueError, match="ordinary predecessor cutover"): + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=_predecessor(superseded_at=CUTOVER), + evidence_version=2, + **_successor_values(superseded_at=OTHER_CUTOVER), + ) + + +def test_successor_is_invalid_when_ordinary_predecessor_has_no_cutover() -> None: + """A separate edge must not manufacture currentness absent from the ordinary record.""" + with pytest.raises(ValueError, match="ordinary predecessor cutover"): + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=_predecessor(superseded_at=None), + evidence_version=2, + **_successor_values(superseded_at=CUTOVER), + ) + + +def test_current_v2_projection_rejects_a_predecessor_already_marked_superseded() -> None: + """Omitting successor coordinates must not hide an owner-resolved ordinary cutover.""" + with pytest.raises(ValueError, match="ordinary predecessor cutover"): + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=_predecessor(superseded_at=CUTOVER), + evidence_version=2, + ) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_lookup_key_completeness.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_lookup_key_completeness.py new file mode 100644 index 000000000..ece349f5b --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_lookup_key_completeness.py @@ -0,0 +1,44 @@ +"""Fail closed when v2 exact-artifact correction lookup omits predecessor artifact identity.""" + +from inspect import signature + +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort, + resolve_validation_result_nonverifiability_supersession_v2_authority, +) + + +_REQUIRED_FAILED_ARTIFACT_COORDINATES = ( + "failed_evidence_reference", + "failed_evidence_digest", +) + + +def test_v2_resolver_requires_exact_failed_artifact_coordinates() -> None: + """Bind callers to the immutable failed artifact that the v2 correction supersedes.""" + parameters = signature( + resolve_validation_result_nonverifiability_supersession_v2_authority + ).parameters + for coordinate in _REQUIRED_FAILED_ARTIFACT_COORDINATES: + assert coordinate in parameters + + +def test_v2_owner_read_port_keys_exact_failed_artifact_coordinates() -> None: + """Prevent same-family artifacts from sharing an ambiguous v2 owner lookup prefix.""" + parameters = signature( + ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort.read_validation_result_nonverifiability_supersession_v2_authority + ).parameters + for coordinate in _REQUIRED_FAILED_ARTIFACT_COORDINATES: + assert coordinate in parameters + + +def test_failed_artifact_release_time_remains_owner_evidence() -> None: + """Keep failed-artifact chronology owner-resolved once reference and digest are exact.""" + resolver_parameters = signature( + resolve_validation_result_nonverifiability_supersession_v2_authority + ).parameters + port_parameters = signature( + ValidationResultNonVerifiabilitySupersessionV2AuthorityReadPort.read_validation_result_nonverifiability_supersession_v2_authority + ).parameters + assert "failed_evidence_released_at" not in resolver_parameters + assert "failed_evidence_released_at" not in port_parameters diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_predecessor_integrity.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_predecessor_integrity.py new file mode 100644 index 000000000..3d59ac686 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_supersession_v2_predecessor_integrity.py @@ -0,0 +1,166 @@ +"""Require v2 correction authority to revalidate the complete predecessor contract.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api.registry import ValidationPrincipal +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityRecord, +) +from orgmetra_workforce_validation_api.result_nonverifiability_supersession_v2_authority import ( + ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError, + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord, + _READ_FIELDS, + resolve_validation_result_nonverifiability_supersession_v2_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RESULT_REFERENCE = "validation_analysis_result:11111111-1111-4111-8111-111111111111" +FAILED_REFERENCE = "analysis_weight_receipt:22222222-2222-4222-8222-222222222222" +ATTEMPT_REFERENCE = "validation_evidence_verification_attempt:33333333-3333-4333-8333-333333333333" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +RESULT_DIGEST = "1" * 64 +FAILED_DIGEST = "2" * 64 +ATTEMPT_DIGEST = "3" * 64 +OWNER_DIGEST = "5" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 5, 55, tzinfo=timezone.utc) +FAILED_RELEASED_AT = datetime(2026, 9, 17, 5, 59, tzinfo=timezone.utc) +EVALUATED_AT = datetime(2026, 9, 17, 6, 0, tzinfo=timezone.utc) +ATTEMPT_RELEASED_AT = datetime(2026, 9, 17, 6, 2, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 6, 5, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 6, 10, tzinfo=timezone.utc) + + +def _predecessor() -> ValidationResultNonVerifiabilityRecord: + """Build one canonical non-reproducible predecessor before structural corruption.""" + return ValidationResultNonVerifiabilityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failure_mode="non_reproducible", + failed_evidence_reference=FAILED_REFERENCE, + failed_evidence_digest=FAILED_DIGEST, + failed_evidence_released_at=FAILED_RELEASED_AT, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + verification_attempt_released_at=ATTEMPT_RELEASED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + evaluated_at=EVALUATED_AT, + released_at=RELEASED_AT, + ) + + +class _ReadPort: + """Return configured owner evidence through the v2 read capability.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_validation_result_nonverifiability_supersession_v2_authority( + self, **coordinates: object + ) -> object: + """Return configured evidence after accepting caller-known lookup coordinates.""" + del coordinates + return self.result + + +def _policy() -> PurposeBoundAccessPolicy: + """Permit the full owner evidence set used by the v2 resolver.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="validation-result-nonverifiability-supersession-read-v2", + resource_kind="validation_result_nonverifiability_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=_READ_FIELDS, + ) + + +def _principal() -> ValidationPrincipal: + """Return one tenant-bound validation reader.""" + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +@pytest.mark.parametrize( + ("tuple_index", "hostile_value"), + [ + (7, RESULT_DIGEST), + (13, EVALUATED_AT + timedelta(seconds=1)), + (17, EVALUATED_AT + timedelta(seconds=1)), + (18, EVALUATED_AT - timedelta(seconds=1)), + ], +) +def test_v2_revalidates_full_predecessor_invariants( + tuple_index: int, hostile_value: object +) -> None: + """Reject exact-typed predecessors forged around v1 digest or chronology guards.""" + canonical = _predecessor() + forged_values = list(canonical) + forged_values[tuple_index] = hostile_value + forged = tuple.__new__(ValidationResultNonVerifiabilityRecord, forged_values) + + with pytest.raises(ValueError): + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=forged, + evidence_version=2, + ) + + +def test_resolver_revalidates_exact_typed_owner_evidence() -> None: + """Reject a structurally forged v2 record returned directly by an owner adapter.""" + canonical_predecessor = _predecessor() + forged_values = list(canonical_predecessor) + forged_values[13] = EVALUATED_AT + timedelta(seconds=1) + forged_predecessor = tuple.__new__( + ValidationResultNonVerifiabilityRecord, forged_values + ) + canonical_authority = ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord( + predecessor=canonical_predecessor, + evidence_version=2, + ) + forged_authority = tuple.__new__( + ValidationResultNonVerifiabilitySupersessionV2AuthorityRecord, + ( + forged_predecessor, + canonical_authority.evidence_version, + canonical_authority.superseded_at, + canonical_authority.successor_fields, + ), + ) + + with pytest.raises(ValidationResultNonVerifiabilitySupersessionV2AuthorityIntegrityError): + resolve_validation_result_nonverifiability_supersession_v2_authority( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + result_reference=RESULT_REFERENCE, + result_digest=RESULT_DIGEST, + failed_evidence_kind="analysis_weight_receipt", + failed_evidence_reference=FAILED_REFERENCE, + failed_evidence_digest=FAILED_DIGEST, + verification_attempt_reference=ATTEMPT_REFERENCE, + verification_attempt_digest=ATTEMPT_DIGEST, + evidence_version=2, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=7, + owner_contract_digest=OWNER_DIGEST, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=_ReadPort(forged_authority), + ) diff --git a/services/workforce-validation-api/tests/test_validation_result_nonverifiability_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_view_issuance_integrity.py new file mode 100644 index 000000000..6154f3af6 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_nonverifiability_view_issuance_integrity.py @@ -0,0 +1,77 @@ +"""Regression contract for validation-result non-verifiability view issuance integrity.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.result_nonverifiability as authority_module +from orgmetra_workforce_validation_api.result_nonverifiability import ( + ValidationResultNonVerifiabilityIntegrityError, + ValidationResultNonVerifiabilityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_importable_marker_cannot_mint_nonverifiability_view() -> None: + """Keep the non-verifiability view seal outside importable module state.""" + marker_name = "_VALIDATION_RESULT_NONVERIFIABILITY_VIEW_ISSUANCE_MARKER" + assert not hasattr(authority_module, marker_name) + + forged_view = object.__new__(ValidationResultNonVerifiabilityView) + object.__setattr__(forged_view, "_tenant_identity", TENANT.int) + object.__setattr__(forged_view, "_study_identity", STUDY.int) + object.__setattr__(forged_view, "_fields", ()) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises(ValidationResultNonVerifiabilityIntegrityError): + _ = forged_view.fields + + +def test_low_level_tuple_construction_cannot_issue_nonverifiability_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + ValidationResultNonVerifiabilityView, + ( + TENANT.int, + STUDY.int, + (("result_digest", "0" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_nonverifiability_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(ValidationResultNonVerifiabilityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + ValidationResultNonVerifiabilityIntegrityError, + match="was not issued by resolve_validation_result_nonverifiability", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_nonverifiability_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(ValidationResultNonVerifiabilityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + ValidationResultNonVerifiabilityIntegrityError, + match="was not issued by resolve_validation_result_nonverifiability", + ): + _ = forged_view.fields + + +def test_raw_nonverifiability_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(ValidationResultNonVerifiabilityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_validation_result_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_validation_result_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..57b8a82b7 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,57 @@ +"""Regression contract for validation-result supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.result_supersession_authority import ( + ValidationResultSupersessionAuthorityIntegrityError, + ValidationResultSupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_low_level_tuple_construction_cannot_issue_validation_result_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + ValidationResultSupersessionAuthorityView, + (TENANT.int, STUDY.int, (("result_digest", "0" * 64),)), + ) + + +def test_unsealed_object_allocation_cannot_expose_validation_result_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(ValidationResultSupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + ValidationResultSupersessionAuthorityIntegrityError, + match="was not issued by resolve_validation_result_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_validation_result_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(ValidationResultSupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + ValidationResultSupersessionAuthorityIntegrityError, + match="was not issued by resolve_validation_result_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_validation_result_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(ValidationResultSupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_validation_result_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_validation_result_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..fd9367834 --- /dev/null +++ b/services/workforce-validation-api/tests/test_validation_result_supersession_authority_view_seal_capability.py @@ -0,0 +1,58 @@ +"""Hostile sealing-capability regression for validation-result supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.result_supersession_authority as authority_module +from orgmetra_workforce_validation_api.result_supersession_authority import ( + ValidationResultSupersessionAuthorityIntegrityError, + ValidationResultSupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000427") +STUDY = UUID("00000000-0000-0000-0000-000000000428") + + +def _raw_view_with_module_marker() -> ValidationResultSupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(ValidationResultSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__( + view, + "_fields", + (("result_digest", "b" * 64),), + ) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_VALIDATION_RESULT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_validation_result_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_VALIDATION_RESULT_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_validation_result_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + ValidationResultSupersessionAuthorityIntegrityError, + match=( + "validation result supersession view was not issued by " + "resolve_validation_result_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_view_issuance_integrity.py new file mode 100644 index 000000000..a935bcd18 --- /dev/null +++ b/services/workforce-validation-api/tests/test_view_issuance_integrity.py @@ -0,0 +1,69 @@ +"""Regression contract for workforce-validation authorized-view issuance.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.registry as registry + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") + + +def test_direct_authorized_view_construction_fails_closed() -> None: + """Require purpose-bound reads, not public construction, to issue study views.""" + with pytest.raises(TypeError, match="issued only by read_validity_study"): + registry.ValidityStudyView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(("study_status_code", "study_draft"),), + ) + + +def test_registry_module_exposes_no_unconditional_view_issuer() -> None: + """Keep ordinary view issuance inside the authorized read application path.""" + assert not hasattr(registry, "_issue_validity_study_view") + + +def test_low_level_tuple_construction_cannot_issue_study_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + registry.ValidityStudyView, + (TENANT.int, STUDY.int, (("study_status_code", "study_draft"),)), + ) + + +def test_unsealed_object_allocation_cannot_expose_study_view() -> None: + """Require the read-path seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(registry.ValidityStudyView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + registry.ValidityStudyIntegrityError, + match="was not issued by read_validity_study", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_study_view() -> None: + """Reject marker-shaped objects that did not originate from the read path.""" + forged_view = object.__new__(registry.ValidityStudyView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + registry.ValidityStudyIntegrityError, + match="was not issued by read_validity_study", + ): + _ = forged_view.fields + + +def test_raw_study_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(registry.ValidityStudyView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_authority.py b/services/workforce-validation-api/tests/test_weight_eligibility_authority.py new file mode 100644 index 000000000..4b976d826 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_authority.py @@ -0,0 +1,346 @@ +"""Fail-closed contract for released cross-sectional/longitudinal weight eligibility.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_eligibility_authority import ( + WeightEligibilityAuthorityIntegrityError, + WeightEligibilityAuthorityNotFound, + WeightEligibilityAuthorityReadPort, + WeightEligibilityAuthorityRecord, + WeightEligibilityAuthorityView, + resolve_weight_eligibility_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT_REFERENCE = "weight_eligibility_receipt:11111111-1111-4111-8111-111111111111" +TARGET_POPULATION_REFERENCE = "analysis_target_population:workers-2026q3" +REFERENCE_DURATION_REFERENCE = "analysis_reference_duration:2026q3" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RECEIPT_DIGEST = "1" * 64 +TARGET_POPULATION_DIGEST = "2" * 64 +REFERENCE_DURATION_DIGEST = "3" * 64 +ELIGIBLE_CASE_SET_DIGEST = "4" * 64 +WEIGHT_ARTIFACT_DIGEST = "5" * 64 +OWNER_CONTRACT_DIGEST = "6" * 64 +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "weight_scope_code", + "target_population_reference", + "target_population_digest", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured eligibility authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_weight_eligibility_authority(self, **coordinates: object) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(WeightEligibilityAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that must be rejected without executing it.""" + + @property + def read_weight_eligibility_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-eligibility-authority-read-v2", + resource_kind="weight_eligibility_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> WeightEligibilityAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "eligibility_receipt_reference": RECEIPT_REFERENCE, + "eligibility_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "weight_scope_code": "longitudinal", + "target_population_reference": TARGET_POPULATION_REFERENCE, + "target_population_digest": TARGET_POPULATION_DIGEST, + "reference_duration_reference": REFERENCE_DURATION_REFERENCE, + "reference_duration_digest": REFERENCE_DURATION_DIGEST, + "eligible_case_set_digest": ELIGIBLE_CASE_SET_DIGEST, + "weight_artifact_digest": WEIGHT_ARTIFACT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return WeightEligibilityAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> WeightEligibilityAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "eligibility_receipt_reference": RECEIPT_REFERENCE, + "eligibility_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "weight_scope_code": "longitudinal", + "target_population_reference": TARGET_POPULATION_REFERENCE, + "target_population_digest": TARGET_POPULATION_DIGEST, + "reference_duration_reference": REFERENCE_DURATION_REFERENCE, + "reference_duration_digest": REFERENCE_DURATION_DIGEST, + "eligible_case_set_digest": ELIGIBLE_CASE_SET_DIGEST, + "weight_artifact_digest": WEIGHT_ARTIFACT_DIGEST, + "constructed_at": CONSTRUCTED_AT, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_weight_eligibility_authority(**values) + + +def test_longitudinal_resolution_binds_population_duration_case_set_and_artifact() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, WeightEligibilityAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["weight_scope_code"] == "longitudinal" + assert port.calls[0]["reference_duration_reference"] == REFERENCE_DURATION_REFERENCE + assert port.calls[0]["eligible_case_set_digest"] == ELIGIBLE_CASE_SET_DIGEST + assert port.calls[0]["weight_artifact_digest"] == WEIGHT_ARTIFACT_DIGEST + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + assert ("weight_scope_code", "longitudinal") in view.fields + assert ("target_population_reference", TARGET_POPULATION_REFERENCE) in view.fields + assert ("reference_duration_reference", REFERENCE_DURATION_REFERENCE) in view.fields + assert ("eligible_case_set_digest", ELIGIBLE_CASE_SET_DIGEST) in view.fields + assert ("weight_artifact_digest", WEIGHT_ARTIFACT_DIGEST) in view.fields + assert ("owner_contract_released_at", OWNER_CONTRACT_RELEASED_AT) in view.fields + assert ("released_at", RELEASED_AT) in view.fields + assert ("superseded_at", None) in view.fields + + +def test_cross_sectional_scope_is_distinct_released_authority() -> None: + record = _record(weight_scope_code="cross_sectional") + view = _resolve(read_port=_ReadPort(record), weight_scope_code="cross_sectional") + assert ("weight_scope_code", "cross_sectional") in view.fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + with pytest.raises(WeightEligibilityAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"eligibility_receipt_digest": "a" * 64}, + {"weight_scope_code": "cross_sectional"}, + {"target_population_reference": "analysis_target_population:other"}, + {"target_population_digest": "b" * 64}, + {"reference_duration_reference": "analysis_reference_duration:other"}, + {"reference_duration_digest": "c" * 64}, + {"eligible_case_set_digest": "d" * 64}, + {"weight_artifact_digest": "e" * 64}, + {"constructed_at": CONSTRUCTED_AT + timedelta(seconds=1)}, + {"owner_contract_version": 4}, + {"owner_contract_digest": "f" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_release_chronology_and_use_fail_closed() -> None: + with pytest.raises(ValueError): + _record(released_at=CONSTRUCTED_AT - timedelta(seconds=1)) + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("eligibility_receipt_reference", "wrong:receipt", ValueError), + ("eligibility_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("weight_scope_code", "panel", ValueError), + ("target_population_reference", "wrong:population", ValueError), + ("target_population_digest", "2" * 63, ValueError), + ("reference_duration_reference", "wrong:duration", ValueError), + ("reference_duration_digest", "3" * 65, ValueError), + ("eligible_case_set_digest", "4" * 63, ValueError), + ("weight_artifact_digest", "5" * 65, ValueError), + ("constructed_at", datetime(2026, 9, 16, 12, 0), ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "6" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.owner_contract_released_at == OWNER_CONTRACT_RELEASED_AT + assert record.superseded_at is None + with pytest.raises(AttributeError): + object.__setattr__(record, "weight_scope_code", "cross_sectional") + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + WeightEligibilityAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_view_rejects_tuple_base_constructor_forgery() -> None: + """Reject caller-authored instances created through the tuple base class.""" + with pytest.raises(TypeError): + tuple.__new__( + WeightEligibilityAuthorityView, + (TENANT, STUDY, (("weight_scope_code", "longitudinal"),)), + ) + + +def test_raw_view_allocation_cannot_expose_projection_state() -> None: + """Keep an unissued raw allocation unusable through every public property.""" + forged = object.__new__(WeightEligibilityAuthorityView) + + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _ = forged.tenant_record_id + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _ = forged.validity_study_id + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _ = forged.fields + + +def test_view_rejects_caller_authored_issuance_marker() -> None: + """Reject a raw allocation even when a caller invents a marker value.""" + forged = object.__new__(WeightEligibilityAuthorityView) + object.__setattr__(forged, "_tenant_identity", TENANT) + object.__setattr__(forged, "_study_identity", STUDY) + object.__setattr__(forged, "_fields", ()) + object.__setattr__(forged, "_issuance_marker", object()) + + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _ = forged.fields + + +def test_issued_view_rejects_mutation_and_deletion() -> None: + """Keep a resolver-issued view immutable after all owner checks complete.""" + view = _resolve(read_port=_ReadPort(_record())) + + with pytest.raises(AttributeError): + view._fields = () + with pytest.raises(AttributeError): + del view._fields diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_authority_chronology.py b/services/workforce-validation-api/tests/test_weight_eligibility_authority_chronology.py new file mode 100644 index 000000000..f9cebdaa0 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_authority_chronology.py @@ -0,0 +1,168 @@ +"""Chronology contract for released weight-eligibility authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_eligibility_authority import ( + WeightEligibilityAuthorityIntegrityError, + WeightEligibilityAuthorityRecord, + resolve_weight_eligibility_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000c1") +RECEIPT_REFERENCE = "weight_eligibility_receipt:eligibility-chronology-1" +TARGET_REFERENCE = "analysis_target_population:population-1" +DURATION_REFERENCE = "analysis_reference_duration:duration-1" +OWNER_REFERENCE = "released_owner_contract:weight-eligibility-v1" +RECEIPT_DIGEST = "1" * 64 +TARGET_DIGEST = "2" * 64 +DURATION_DIGEST = "3" * 64 +CASE_SET_DIGEST = "4" * 64 +ARTIFACT_DIGEST = "5" * 64 +OWNER_DIGEST = "6" * 64 +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +SUPERSEDED_AT = datetime(2026, 9, 17, 13, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "weight_scope_code", + "target_population_reference", + "target_population_digest", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + def __init__(self, record: WeightEligibilityAuthorityRecord) -> None: + self.record = record + + def read_weight_eligibility_authority(self, **_: object) -> WeightEligibilityAuthorityRecord: + return self.record + + +def _record( + *, + owner_contract_released_at: datetime = OWNER_RELEASED_AT, + superseded_at: datetime | None = SUPERSEDED_AT, +) -> WeightEligibilityAuthorityRecord: + return WeightEligibilityAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT_REFERENCE, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + weight_scope_code="longitudinal", + target_population_reference=TARGET_REFERENCE, + target_population_digest=TARGET_DIGEST, + reference_duration_reference=DURATION_REFERENCE, + reference_duration_digest=DURATION_DIGEST, + eligible_case_set_digest=CASE_SET_DIGEST, + weight_artifact_digest=ARTIFACT_DIGEST, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=owner_contract_released_at, + released_at=RELEASED_AT, + superseded_at=superseded_at, + ) + + +def _policy() -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-eligibility-chronology-v2", + resource_kind="weight_eligibility_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _resolve(*, used_at: datetime, record: WeightEligibilityAuthorityRecord) -> object: + return resolve_weight_eligibility_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT_REFERENCE, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + weight_scope_code="longitudinal", + target_population_reference=TARGET_REFERENCE, + target_population_digest=TARGET_DIGEST, + reference_duration_reference=DURATION_REFERENCE, + reference_duration_digest=DURATION_DIGEST, + eligible_case_set_digest=CASE_SET_DIGEST, + weight_artifact_digest=ARTIFACT_DIGEST, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=_ReadPort(record), + ) + + +def test_chronology_is_owner_evidence_not_caller_input() -> None: + parameters = signature(resolve_weight_eligibility_authority).parameters + assert "owner_contract_released_at" not in parameters + assert "superseded_at" not in parameters + + +def test_owner_contract_cannot_retroactively_authorize_eligibility() -> None: + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(microseconds=1)) + + +def test_historical_use_before_cutover_remains_reproducible() -> None: + view = _resolve(used_at=SUPERSEDED_AT - timedelta(microseconds=1), record=_record()) + fields = dict(view.fields) + assert fields["owner_contract_released_at"] == OWNER_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert fields["superseded_at"] == SUPERSEDED_AT + + +def test_use_at_or_after_owner_cutover_fails_closed() -> None: + for used_at in (SUPERSEDED_AT, SUPERSEDED_AT + timedelta(seconds=1)): + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _resolve(used_at=used_at, record=_record()) + + +def test_non_positive_owner_authority_interval_is_rejected() -> None: + for superseded_at in (RELEASED_AT, RELEASED_AT - timedelta(microseconds=1)): + with pytest.raises(ValueError): + _record(superseded_at=superseded_at) + + +def test_unsuperseded_owner_evidence_remains_current() -> None: + _resolve(used_at=SUPERSEDED_AT + timedelta(days=30), record=_record(superseded_at=None)) diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_authority_edges.py b/services/workforce-validation-api/tests/test_weight_eligibility_authority_edges.py new file mode 100644 index 000000000..a749514ca --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_authority_edges.py @@ -0,0 +1,57 @@ +"""Hostile edges for released weight-eligibility authority.""" + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.weight_eligibility_authority import ( + WeightEligibilityAuthorityRecord, +) + + +def _record( + *, + evidence_version: object = 1, + owner_contract_released_at: object = datetime( + 2026, 9, 16, 12, 30, tzinfo=timezone.utc + ), + superseded_at: object = None, +) -> WeightEligibilityAuthorityRecord: + return WeightEligibilityAuthorityRecord( + tenant_record_id=UUID("10000000-0000-7000-8000-000000000001"), + validity_study_id=UUID("00000000-0000-7000-8000-0000000000d1"), + eligibility_receipt_reference=( + "weight_eligibility_receipt:11111111-1111-4111-8111-111111111111" + ), + eligibility_receipt_digest="1" * 64, + evidence_version=evidence_version, + weight_scope_code="longitudinal", + target_population_reference="analysis_target_population:workers-2026q3", + target_population_digest="2" * 64, + reference_duration_reference="analysis_reference_duration:2026q3", + reference_duration_digest="3" * 64, + eligible_case_set_digest="4" * 64, + weight_artifact_digest="5" * 64, + constructed_at=datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc), + owner_contract_reference=( + "released_owner_contract:22222222-2222-4222-8222-222222222222" + ), + owner_contract_version=3, + owner_contract_digest="6" * 64, + owner_contract_released_at=owner_contract_released_at, + released_at=datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc), + superseded_at=superseded_at, + ) + + +def test_evidence_version_cannot_advance_without_contract_revision() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + + +def test_owner_chronology_requires_timezone_aware_instants() -> None: + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 16, 12, 30)) + with pytest.raises(ValueError): + _record(superseded_at=datetime(2026, 9, 17, 13, 0)) diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_weight_eligibility_authority_structural_integrity.py new file mode 100644 index 000000000..1f153852b --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_authority_structural_integrity.py @@ -0,0 +1,151 @@ +"""Structural-integrity regressions for weight-eligibility owner evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_eligibility_authority import ( + WeightEligibilityAuthorityIntegrityError, + WeightEligibilityAuthorityRecord, + resolve_weight_eligibility_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT_REFERENCE = "weight_eligibility_receipt:11111111-1111-4111-8111-111111111111" +TARGET_POPULATION_REFERENCE = "analysis_target_population:workers-2026q3" +REFERENCE_DURATION_REFERENCE = "analysis_reference_duration:2026q3" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RECEIPT_DIGEST = "1" * 64 +TARGET_POPULATION_DIGEST = "2" * 64 +REFERENCE_DURATION_DIGEST = "3" * 64 +ELIGIBLE_CASE_SET_DIGEST = "4" * 64 +WEIGHT_ARTIFACT_DIGEST = "5" * 64 +OWNER_CONTRACT_DIGEST = "6" * 64 +CONSTRUCTED_AT = datetime(2026, 9, 16, 12, 0, tzinfo=timezone.utc) +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 9, 16, 12, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 16, 13, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "weight_scope_code", + "target_population_reference", + "target_population_digest", + "reference_duration_reference", + "reference_duration_digest", + "eligible_case_set_digest", + "weight_artifact_digest", + "constructed_at", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return configured owner evidence without normalizing tuple structure.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_weight_eligibility_authority(self, **_: object) -> object: + """Return the configured raw owner result.""" + return self.result + + +def _record() -> WeightEligibilityAuthorityRecord: + """Build one valid canonical weight-eligibility authority record.""" + return WeightEligibilityAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT_REFERENCE, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + weight_scope_code="longitudinal", + target_population_reference=TARGET_POPULATION_REFERENCE, + target_population_digest=TARGET_POPULATION_DIGEST, + reference_duration_reference=REFERENCE_DURATION_REFERENCE, + reference_duration_digest=REFERENCE_DURATION_DIGEST, + eligible_case_set_digest=ELIGIBLE_CASE_SET_DIGEST, + weight_artifact_digest=WEIGHT_ARTIFACT_DIGEST, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=OWNER_CONTRACT_RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def _resolve(read_port: object) -> object: + """Resolve canonical coordinates through a supplied raw owner port.""" + principal = ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-eligibility-authority-read-v2", + resource_kind="weight_eligibility_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + return resolve_weight_eligibility_authority( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT_REFERENCE, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + weight_scope_code="longitudinal", + target_population_reference=TARGET_POPULATION_REFERENCE, + target_population_digest=TARGET_POPULATION_DIGEST, + reference_duration_reference=REFERENCE_DURATION_REFERENCE, + reference_duration_digest=REFERENCE_DURATION_DIGEST, + eligible_case_set_digest=ELIGIBLE_CASE_SET_DIGEST, + weight_artifact_digest=WEIGHT_ARTIFACT_DIGEST, + constructed_at=CONSTRUCTED_AT, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=read_port, + ) + + +def test_owner_port_cannot_append_hidden_tuple_fields() -> None: + """Reject exact-typed evidence with coordinates outside the canonical tuple.""" + canonical = _record() + forged = tuple.__new__( + WeightEligibilityAuthorityRecord, + (*tuple(canonical), "hidden-unreviewed-owner-coordinate"), + ) + + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _resolve(_ReadPort(forged)) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + """Map truncated exact-typed evidence to the domain integrity boundary.""" + canonical = _record() + forged = tuple.__new__(WeightEligibilityAuthorityRecord, tuple(canonical)[:-1]) + + with pytest.raises(WeightEligibilityAuthorityIntegrityError): + _resolve(_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_weight_eligibility_authority_view_seal_capability.py new file mode 100644 index 000000000..9cb39e1a8 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_authority_view_seal_capability.py @@ -0,0 +1,44 @@ +"""Hostile sealing-capability regression for weight-eligibility views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.weight_eligibility_authority as authority_module +from orgmetra_workforce_validation_api.weight_eligibility_authority import ( + WeightEligibilityAuthorityIntegrityError, + WeightEligibilityAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000433") +STUDY = UUID("00000000-0000-0000-0000-000000000434") + + +def _raw_view_with_module_marker() -> WeightEligibilityAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(WeightEligibilityAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__(view, "_fields", (("weight_artifact_digest", "b" * 64),)) + object.__setattr__( + view, + "_issuance_marker", + getattr(authority_module, "_WEIGHT_ELIGIBILITY_VIEW_ISSUANCE_MARKER", object()), + ) + return view + + +def test_module_exposes_no_weight_eligibility_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr(authority_module, "_WEIGHT_ELIGIBILITY_VIEW_ISSUANCE_MARKER") + + +def test_importable_marker_cannot_mint_weight_eligibility_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + WeightEligibilityAuthorityIntegrityError, + match="weight-eligibility authority view was not issued by the resolver", + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority.py b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority.py new file mode 100644 index 000000000..15413b2d1 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority.py @@ -0,0 +1,257 @@ +"""Fail closed when released weight-eligibility evidence has a successor.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_eligibility_supersession_authority import ( + WeightEligibilitySupersessionAuthorityIntegrityError, + WeightEligibilitySupersessionAuthorityRecord, + resolve_weight_eligibility_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT_REFERENCE = "weight_eligibility_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR_REFERENCE = "weight_eligibility_receipt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +CUTOVER_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_eligibility_receipt_reference", + "successor_eligibility_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return one configured correction state through the owner read shape.""" + + def __init__(self, record: WeightEligibilitySupersessionAuthorityRecord) -> None: + self.record = record + + def read_weight_eligibility_supersession_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + eligibility_receipt_reference: str, + eligibility_receipt_digest: str, + evidence_version: int, + owner_contract_reference: str, + owner_contract_version: int, + owner_contract_digest: str, + ) -> WeightEligibilitySupersessionAuthorityRecord: + """Return owner evidence; the resolver verifies every current coordinate.""" + del ( + tenant_record_id, + validity_study_id, + eligibility_receipt_reference, + eligibility_receipt_digest, + evidence_version, + owner_contract_reference, + owner_contract_version, + owner_contract_digest, + ) + return self.record + + +def _principal() -> ValidationPrincipal: + """Return one exact workforce-validation principal.""" + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy() -> PurposeBoundAccessPolicy: + """Authorize the full internal correction evidence while minimizing the view.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-eligibility-supersession-read-v1", + resource_kind="weight_eligibility_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record( + *, + superseded_at: datetime | None = CUTOVER_AT, + successor_reference: str | None = SUCCESSOR_REFERENCE, + successor_digest: str | None = SUCCESSOR_DIGEST, + successor_evidence_version: int | None = 1, + successor_released_at: datetime | None = CUTOVER_AT, + owner_contract_released_at: datetime = OWNER_CONTRACT_RELEASED_AT, +) -> WeightEligibilitySupersessionAuthorityRecord: + """Build one canonical current or superseded eligibility authority record.""" + return WeightEligibilitySupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT_REFERENCE, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + owner_contract_released_at=owner_contract_released_at, + released_at=RELEASED_AT, + superseded_at=superseded_at, + successor_eligibility_receipt_reference=successor_reference, + successor_eligibility_receipt_digest=successor_digest, + successor_evidence_version=successor_evidence_version, + successor_released_at=successor_released_at, + ) + + +def _resolve( + record: WeightEligibilitySupersessionAuthorityRecord, + *, + used_at: datetime, +): + """Resolve one historical eligibility authority instant.""" + return resolve_weight_eligibility_supersession_authority( + principal=_principal(), + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT_REFERENCE, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER_CONTRACT_REFERENCE, + owner_contract_version=3, + owner_contract_digest=OWNER_CONTRACT_DIGEST, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=_policy(), + read_port=_ReadPort(record), + ) + + +def test_successor_chronology_is_owner_evidence_not_caller_input() -> None: + """Keep cutover and successor coordinates out of caller-controlled resolution.""" + parameters = signature(resolve_weight_eligibility_supersession_authority).parameters + assert "owner_contract_released_at" not in parameters + assert "released_at" not in parameters + assert "superseded_at" not in parameters + assert "successor_eligibility_receipt_reference" not in parameters + assert "successor_eligibility_receipt_digest" not in parameters + assert "successor_evidence_version" not in parameters + assert "successor_released_at" not in parameters + + +def test_historical_use_before_cutover_remains_verifiable_without_successor_disclosure() -> None: + """Allow predecessor reconstruction before cutover without leaking successor evidence.""" + view = _resolve(_record(), used_at=CUTOVER_AT - timedelta(microseconds=1)) + fields = dict(view.fields) + assert fields["eligibility_receipt_reference"] == RECEIPT_REFERENCE + assert fields["eligibility_receipt_digest"] == RECEIPT_DIGEST + assert fields["released_at"] == RELEASED_AT + assert "superseded_at" not in fields + assert "successor_eligibility_receipt_reference" not in fields + assert "successor_released_at" not in fields + + +def test_use_at_or_after_cutover_fails_closed() -> None: + """Reject stale eligibility evidence at the exact successor cutover and later.""" + record = _record() + for used_at in (CUTOVER_AT, CUTOVER_AT + timedelta(seconds=1)): + with pytest.raises(WeightEligibilitySupersessionAuthorityIntegrityError): + _resolve(record, used_at=used_at) + + +def test_owner_contract_cannot_retroactively_authorize_receipt() -> None: + """Require the governing contract to exist before eligibility release.""" + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED_AT + timedelta(microseconds=1)) + + +def test_unsuperseded_receipt_remains_current() -> None: + """Keep a released receipt current when no complete successor edge exists.""" + record = _record( + superseded_at=None, + successor_reference=None, + successor_digest=None, + successor_evidence_version=None, + successor_released_at=None, + ) + _resolve(record, used_at=CUTOVER_AT + timedelta(days=30)) + + +@pytest.mark.parametrize( + ( + "superseded_at", + "successor_reference", + "successor_digest", + "successor_evidence_version", + "successor_released_at", + ), + [ + (CUTOVER_AT, None, SUCCESSOR_DIGEST, 1, CUTOVER_AT), + (None, SUCCESSOR_REFERENCE, SUCCESSOR_DIGEST, 1, CUTOVER_AT), + (CUTOVER_AT, RECEIPT_REFERENCE, SUCCESSOR_DIGEST, 1, CUTOVER_AT), + (CUTOVER_AT, SUCCESSOR_REFERENCE, RECEIPT_DIGEST, 1, CUTOVER_AT), + (CUTOVER_AT, SUCCESSOR_REFERENCE, SUCCESSOR_DIGEST, 2, CUTOVER_AT), + ( + RELEASED_AT, + SUCCESSOR_REFERENCE, + SUCCESSOR_DIGEST, + 1, + RELEASED_AT, + ), + ( + CUTOVER_AT, + SUCCESSOR_REFERENCE, + SUCCESSOR_DIGEST, + 1, + CUTOVER_AT - timedelta(microseconds=1), + ), + ( + CUTOVER_AT, + SUCCESSOR_REFERENCE, + SUCCESSOR_DIGEST, + 1, + CUTOVER_AT + timedelta(microseconds=1), + ), + ], +) +def test_supersession_requires_one_complete_atomic_successor_edge( + superseded_at: datetime | None, + successor_reference: str | None, + successor_digest: str | None, + successor_evidence_version: int | None, + successor_released_at: datetime | None, +) -> None: + """Reject partial, self-referential, schema-floating, or non-atomic successor evidence.""" + with pytest.raises(ValueError): + _record( + superseded_at=superseded_at, + successor_reference=successor_reference, + successor_digest=successor_digest, + successor_evidence_version=successor_evidence_version, + successor_released_at=successor_released_at, + ) diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_edges.py b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_edges.py new file mode 100644 index 000000000..a7d6607a1 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_edges.py @@ -0,0 +1,309 @@ +"""Hostile edges for append-only weight-eligibility correction authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_eligibility_supersession_authority import ( + WeightEligibilitySupersessionAuthorityIntegrityError, + WeightEligibilitySupersessionAuthorityNotFound, + WeightEligibilitySupersessionAuthorityReadPort, + WeightEligibilitySupersessionAuthorityRecord, + WeightEligibilitySupersessionAuthorityView, + resolve_weight_eligibility_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +RECEIPT_REFERENCE = "weight_eligibility_receipt:11111111-1111-4111-8111-111111111111" +OTHER_RECEIPT_REFERENCE = "weight_eligibility_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" +SUCCESSOR_REFERENCE = "weight_eligibility_receipt:33333333-3333-4333-8333-333333333333" +OWNER_CONTRACT_REFERENCE = "released_owner_contract:22222222-2222-4222-8222-222222222222" +OTHER_OWNER_CONTRACT_REFERENCE = "released_owner_contract:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "3" * 64 +OWNER_CONTRACT_DIGEST = "2" * 64 +OWNER_CONTRACT_RELEASED_AT = datetime(2026, 7, 1, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 7, 15, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_eligibility_receipt_reference", + "successor_eligibility_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured authority and retain lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_weight_eligibility_supersession_authority(self, **coordinates: object) -> object: + """Capture the owner lookup and return configured evidence.""" + self.calls.append(dict(coordinates)) + return self.result + + +class _NoReadMethod: + """Deliberately fail the owner-port protocol.""" + + +class _ProtocolOnly(WeightEligibilitySupersessionAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +class _DescriptorReadPort: + """Expose a descriptor that static capability validation must reject.""" + + @property + def read_weight_eligibility_supersession_authority(self) -> object: + """Trip if dependency validation executes the descriptor.""" + raise AssertionError("descriptor must not execute") + + +def _principal(*, tenant_record_id: UUID = TENANT) -> ValidationPrincipal: + """Return one exact workforce-validation principal.""" + return ValidationPrincipal( + tenant_record_id=tenant_record_id, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + """Return the purpose-bound policy for correction evidence.""" + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-eligibility-supersession-authority-read-v1", + resource_kind="weight_eligibility_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> WeightEligibilitySupersessionAuthorityRecord: + """Build one current eligibility correction state.""" + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "eligibility_receipt_reference": RECEIPT_REFERENCE, + "eligibility_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "owner_contract_released_at": OWNER_CONTRACT_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + "successor_eligibility_receipt_reference": None, + "successor_eligibility_receipt_digest": None, + "successor_evidence_version": None, + "successor_released_at": None, + } + values.update(overrides) + return WeightEligibilitySupersessionAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> WeightEligibilitySupersessionAuthorityView: + """Resolve current eligibility authority with caller-known coordinates only.""" + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "eligibility_receipt_reference": RECEIPT_REFERENCE, + "eligibility_receipt_digest": RECEIPT_DIGEST, + "evidence_version": 1, + "owner_contract_reference": OWNER_CONTRACT_REFERENCE, + "owner_contract_version": 3, + "owner_contract_digest": OWNER_CONTRACT_DIGEST, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_weight_eligibility_supersession_authority(**values) + + +def test_current_receipt_resolution_uses_owner_chronology_without_successor() -> None: + """Resolve a current receipt and keep chronology out of the lookup key.""" + port = _ReadPort(_record()) + view = _resolve(read_port=port) + + assert isinstance(port, WeightEligibilitySupersessionAuthorityReadPort) + assert len(port.calls) == 1 + assert port.calls[0]["eligibility_receipt_reference"] == RECEIPT_REFERENCE + assert "owner_contract_released_at" not in port.calls[0] + assert "released_at" not in port.calls[0] + assert "superseded_at" not in port.calls[0] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["owner_contract_released_at"] == OWNER_CONTRACT_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert "superseded_at" not in fields + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + """Do not consult owner evidence when purpose authorization fails.""" + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +def test_missing_or_noncanonical_owner_evidence_fails_closed() -> None: + """Reject absent and non-canonical owner evidence.""" + with pytest.raises(WeightEligibilitySupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(WeightEligibilitySupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"eligibility_receipt_reference": OTHER_RECEIPT_REFERENCE}, + {"eligibility_receipt_digest": "a" * 64}, + {"owner_contract_reference": OTHER_OWNER_CONTRACT_REFERENCE}, + {"owner_contract_version": 4}, + {"owner_contract_digest": "b" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate( + record_overrides: dict[str, object] +) -> None: + """Fail closed if the owner returns a different current receipt coordinate.""" + with pytest.raises(WeightEligibilitySupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides))) + + +def test_release_chronology_and_historical_use_are_distinct() -> None: + """Reject pre-release use while preserving history before a later cutover.""" + with pytest.raises(WeightEligibilitySupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(released_at=USED_AT + timedelta(seconds=1)))) + + cutover = USED_AT + timedelta(seconds=2) + record = _record( + superseded_at=cutover, + successor_eligibility_receipt_reference=SUCCESSOR_REFERENCE, + successor_eligibility_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=cutover, + ) + view = _resolve(read_port=_ReadPort(record)) + assert dict(view.fields)["eligibility_receipt_digest"] == RECEIPT_DIGEST + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("eligibility_receipt_reference", "wrong:receipt", ValueError), + ("eligibility_receipt_digest", "ABC", ValueError), + ("evidence_version", False, ValueError), + ("evidence_version", 2, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "2" * 63, ValueError), + ("used_at", datetime(2026, 9, 17), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + """Validate caller-controlled coordinates before touching the owner port.""" + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **overrides) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_rejects_non_v1_evidence_and_public_view_construction() -> None: + """Keep receipt schema fixed and prevent forged minimized views.""" + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(TypeError, match="issued only by"): + WeightEligibilitySupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_successor_chronology_rejects_naive_or_predecessor_time() -> None: + """Require aware, post-predecessor, cutover-aligned successor release evidence.""" + with pytest.raises(ValueError): + _record( + superseded_at=datetime(2026, 9, 17), + successor_eligibility_receipt_reference=SUCCESSOR_REFERENCE, + successor_eligibility_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=datetime(2026, 9, 17, tzinfo=timezone.utc), + ) + with pytest.raises(ValueError): + _record( + superseded_at=USED_AT + timedelta(seconds=1), + successor_eligibility_receipt_reference=SUCCESSOR_REFERENCE, + successor_eligibility_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=RELEASED_AT, + ) + + +def test_record_and_view_are_structurally_immutable_and_uuid_views_are_detached() -> None: + """Detach UUIDs and reject mutation of owner records and minimized views.""" + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + assert record.validity_study_id == STUDY + assert record.released_at == RELEASED_AT + assert record.superseded_at is None + assert record.successor_fields is None + with pytest.raises(AttributeError): + object.__setattr__(record, "released_at", USED_AT) + + view = _resolve(read_port=_ReadPort(record)) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_structural_integrity.py new file mode 100644 index 000000000..2123e11dc --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_structural_integrity.py @@ -0,0 +1,140 @@ +"""Structural-integrity regressions for weight-eligibility supersession evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_eligibility_supersession_authority import ( + WeightEligibilitySupersessionAuthorityIntegrityError, + WeightEligibilitySupersessionAuthorityRecord, + resolve_weight_eligibility_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +RECEIPT = "weight_eligibility_receipt:11111111-1111-4111-8111-111111111111" +SUCCESSOR = "weight_eligibility_receipt:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +RECEIPT_DIGEST = "1" * 64 +SUCCESSOR_DIGEST = "2" * 64 +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 1, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "eligibility_receipt_reference", + "eligibility_receipt_digest", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_eligibility_receipt_reference", + "successor_eligibility_receipt_digest", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured persisted supersession evidence.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_weight_eligibility_supersession_authority(self, **_: object) -> object: + """Return the configured owner evidence.""" + return self.result + + +def _record() -> WeightEligibilitySupersessionAuthorityRecord: + """Build one canonical eligibility correction edge.""" + return WeightEligibilitySupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED_AT, + released_at=RELEASED_AT, + superseded_at=CUTOVER, + successor_eligibility_receipt_reference=SUCCESSOR, + successor_eligibility_receipt_digest=SUCCESSOR_DIGEST, + successor_evidence_version=1, + successor_released_at=CUTOVER, + ) + + +def _resolve(result: object) -> object: + """Resolve the predecessor at a valid pre-cutover use instant.""" + return resolve_weight_eligibility_supersession_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + eligibility_receipt_reference=RECEIPT, + eligibility_receipt_digest=RECEIPT_DIGEST, + evidence_version=1, + owner_contract_reference=OWNER, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + used_at=RELEASED_AT, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-eligibility-supersession-read-v1", + resource_kind="weight_eligibility_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + WeightEligibilitySupersessionAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(WeightEligibilitySupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + WeightEligibilitySupersessionAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(WeightEligibilitySupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_duplicate_nested_current_field_cannot_be_normalized_away() -> None: + canonical = _record() + raw = list(canonical) + raw[2] = canonical.fields + (("eligibility_receipt_reference", RECEIPT),) + forged = tuple.__new__(WeightEligibilitySupersessionAuthorityRecord, tuple(raw)) + + with pytest.raises(WeightEligibilitySupersessionAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..082401c4a --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for weight-eligibility supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.weight_eligibility_supersession_authority import ( + WeightEligibilitySupersessionAuthorityIntegrityError, + WeightEligibilitySupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_low_level_tuple_construction_cannot_issue_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + WeightEligibilitySupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("eligibility_receipt_digest", "6" * 64),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(WeightEligibilitySupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + WeightEligibilitySupersessionAuthorityIntegrityError, + match="was not issued by resolve_weight_eligibility_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(WeightEligibilitySupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + WeightEligibilitySupersessionAuthorityIntegrityError, + match="was not issued by resolve_weight_eligibility_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(WeightEligibilitySupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..efef0f268 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_eligibility_supersession_authority_view_seal_capability.py @@ -0,0 +1,54 @@ +"""Hostile sealing-capability regression for weight-eligibility supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.weight_eligibility_supersession_authority as authority_module +from orgmetra_workforce_validation_api.weight_eligibility_supersession_authority import ( + WeightEligibilitySupersessionAuthorityIntegrityError, + WeightEligibilitySupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000435") +STUDY = UUID("00000000-0000-0000-0000-000000000436") + + +def _raw_view_with_module_marker() -> WeightEligibilitySupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(WeightEligibilitySupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__(view, "_fields", (("eligibility_receipt_digest", "b" * 64),)) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_WEIGHT_ELIGIBILITY_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_weight_eligibility_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_WEIGHT_ELIGIBILITY_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_weight_eligibility_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + WeightEligibilitySupersessionAuthorityIntegrityError, + match=( + "weight-eligibility supersession view was not issued by " + "resolve_weight_eligibility_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_weight_variance_authority.py b/services/workforce-validation-api/tests/test_weight_variance_authority.py new file mode 100644 index 000000000..d2a4d657f --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_authority.py @@ -0,0 +1,319 @@ +"""Fail-closed owner contract for point-weight/variance evidence compatibility.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityIntegrityError, + WeightVarianceAuthorityNotFound, + WeightVarianceAuthorityReadPort, + WeightVarianceAuthorityRecord, + WeightVarianceAuthorityView, + resolve_weight_variance_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +AUTHORITY_REFERENCE = "variance_compatibility_authority:11111111-1111-4111-8111-111111111111" +SAMPLING_REFERENCE = "sampling_design_receipt:22222222-2222-4222-8222-222222222222" +VARIANCE_REFERENCE = "variance_design_receipt:33333333-3333-4333-8333-333333333333" +METHOD_REFERENCE = "variance_method:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +SAMPLING_DIGEST = "1" * 64 +ANALYSIS_WEIGHT_DIGEST = "2" * 64 +CASE_SET_DIGEST = "3" * 64 +ELIGIBILITY_DIGEST = "4" * 64 +CORRECTION_SEQUENCE = 9 +FINAL_WEIGHT_DIGEST = "6" * 64 +VARIANCE_DIGEST = "7" * 64 +OWNER_DIGEST = "8" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 2, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "authority_reference", + "sampling_receipt_reference", + "sampling_receipt_version", + "sampling_receipt_digest", + "analysis_weight_receipt_digest", + "analytic_case_occurrence_set_digest", + "weight_eligibility_receipt_digest", + "weight_correction_sequence", + "final_weight_artifact_digest", + "variance_design_receipt_reference", + "variance_design_receipt_version", + "variance_design_receipt_digest", + "variance_method_reference", + "variance_method_version", + "variance_evidence_mode", + "variance_semantics", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + """Return one configured owner record and retain the exact lookup coordinates.""" + + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[tuple[object, ...]] = [] + + def read_weight_variance_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + sampling_receipt_reference: str, + sampling_receipt_version: int, + sampling_receipt_digest: str, + analysis_weight_receipt_digest: str, + analytic_case_occurrence_set_digest: str, + weight_eligibility_receipt_digest: str, + weight_correction_sequence: int, + final_weight_artifact_digest: str, + variance_design_receipt_reference: str, + variance_design_receipt_version: int, + variance_design_receipt_digest: str, + variance_method_reference: str, + variance_method_version: int, + variance_evidence_mode: str, + variance_semantics: str, + owner_contract_reference: str, + owner_contract_version: int, + ) -> object: + """Capture the owner lookup and return the configured result.""" + self.calls.append( + ( + tenant_record_id, + validity_study_id, + sampling_receipt_reference, + sampling_receipt_version, + sampling_receipt_digest, + analysis_weight_receipt_digest, + analytic_case_occurrence_set_digest, + weight_eligibility_receipt_digest, + weight_correction_sequence, + final_weight_artifact_digest, + variance_design_receipt_reference, + variance_design_receipt_version, + variance_design_receipt_digest, + variance_method_reference, + variance_method_version, + variance_evidence_mode, + variance_semantics, + owner_contract_reference, + owner_contract_version, + ) + ) + return self.result + + +class _ProtocolOnly(WeightVarianceAuthorityReadPort): + """Inherit only the Protocol placeholder, not a concrete owner capability.""" + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-variance-authority-read-v2", + resource_kind="weight_variance_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> WeightVarianceAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY_REFERENCE, + "sampling_receipt_reference": SAMPLING_REFERENCE, + "sampling_receipt_version": 3, + "sampling_receipt_digest": SAMPLING_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "analytic_case_occurrence_set_digest": CASE_SET_DIGEST, + "weight_eligibility_receipt_digest": ELIGIBILITY_DIGEST, + "weight_correction_sequence": CORRECTION_SEQUENCE, + "final_weight_artifact_digest": FINAL_WEIGHT_DIGEST, + "variance_design_receipt_reference": VARIANCE_REFERENCE, + "variance_design_receipt_version": 5, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "variance_method_reference": METHOD_REFERENCE, + "variance_method_version": 2, + "variance_evidence_mode": "replicate_weights", + "variance_semantics": "exact", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 4, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return WeightVarianceAuthorityRecord(**values) + + +def _resolve(*, read_port: object, **overrides: object) -> WeightVarianceAuthorityView: + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "sampling_receipt_reference": SAMPLING_REFERENCE, + "sampling_receipt_version": 3, + "sampling_receipt_digest": SAMPLING_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "analytic_case_occurrence_set_digest": CASE_SET_DIGEST, + "weight_eligibility_receipt_digest": ELIGIBILITY_DIGEST, + "weight_correction_sequence": CORRECTION_SEQUENCE, + "final_weight_artifact_digest": FINAL_WEIGHT_DIGEST, + "variance_design_receipt_reference": VARIANCE_REFERENCE, + "variance_design_receipt_version": 5, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "variance_method_reference": METHOD_REFERENCE, + "variance_method_version": 2, + "variance_evidence_mode": "replicate_weights", + "variance_semantics": "exact", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 4, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_weight_variance_authority(**values) + + +def test_resolution_authorizes_then_returns_owner_corroborated_compatibility() -> None: + port = _ReadPort(_record()) + + view = _resolve(read_port=port) + + assert isinstance(port, WeightVarianceAuthorityReadPort) + assert port.calls == [ + ( + TENANT, + STUDY, + SAMPLING_REFERENCE, + 3, + SAMPLING_DIGEST, + ANALYSIS_WEIGHT_DIGEST, + CASE_SET_DIGEST, + ELIGIBILITY_DIGEST, + CORRECTION_SEQUENCE, + FINAL_WEIGHT_DIGEST, + VARIANCE_REFERENCE, + 5, + VARIANCE_DIGEST, + METHOD_REFERENCE, + 2, + "replicate_weights", + "exact", + OWNER_REFERENCE, + 4, + ) + ] + assert view.tenant_record_id == TENANT + assert view.validity_study_id == STUDY + fields = dict(view.fields) + assert fields["final_weight_artifact_digest"] == FINAL_WEIGHT_DIGEST + assert fields["weight_correction_sequence"] == CORRECTION_SEQUENCE + assert fields["variance_design_receipt_digest"] == VARIANCE_DIGEST + assert fields["owner_contract_digest"] == OWNER_DIGEST + assert fields["owner_contract_released_at"] == OWNER_RELEASED_AT + assert fields["released_at"] == RELEASED_AT + assert fields["superseded_at"] is None + + +def test_authorization_denial_happens_before_owner_resolution() -> None: + port = _ReadPort(_record()) + + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, policy=_policy(purpose_code="audit_review")) + + assert port.calls == [] + + +def test_missing_noncanonical_or_mismatched_owner_evidence_fails_closed() -> None: + with pytest.raises(WeightVarianceAuthorityNotFound): + _resolve(read_port=_ReadPort(None)) + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object())) + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve( + read_port=_ReadPort( + _record(analytic_case_occurrence_set_digest="a" * 64) + ) + ) + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(final_weight_artifact_digest="b" * 64))) + + +def test_point_and_variance_receipts_must_be_distinct() -> None: + with pytest.raises(ValueError): + _record(variance_design_receipt_digest=ANALYSIS_WEIGHT_DIGEST) + with pytest.raises(ValueError): + _resolve( + read_port=_ReadPort(_record()), + variance_design_receipt_digest=ANALYSIS_WEIGHT_DIGEST, + ) + + +def test_approximation_mode_cannot_claim_exact_variance_semantics() -> None: + with pytest.raises(ValueError): + _record(variance_evidence_mode="approximation", variance_semantics="exact") + + +def test_invalid_dependency_and_pre_release_use_fail_closed() -> None: + with pytest.raises(TypeError): + _resolve(read_port=_ProtocolOnly()) + + port = _ReadPort(_record()) + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve(read_port=port, used_at=datetime(2026, 9, 17, 0, 59, tzinfo=timezone.utc)) + assert len(port.calls) == 1 + + +def test_record_and_view_are_immutable_and_public_view_construction_is_blocked() -> None: + record = _record() + with pytest.raises(AttributeError): + object.__setattr__(record, "variance_method_version", 999) + + view = _resolve(read_port=_ReadPort(record)) + with pytest.raises(AttributeError): + object.__setattr__(view, "fields", ()) + with pytest.raises(TypeError): + WeightVarianceAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) + + +def test_cross_tenant_owner_evidence_is_rejected() -> None: + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(tenant_record_id=OTHER_TENANT))) diff --git a/services/workforce-validation-api/tests/test_weight_variance_authority_chronology.py b/services/workforce-validation-api/tests/test_weight_variance_authority_chronology.py new file mode 100644 index 000000000..628fe8458 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_authority_chronology.py @@ -0,0 +1,166 @@ +"""Reject retroactive owner contracts and stale weight/variance compatibility use.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from inspect import signature +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityIntegrityError, + WeightVarianceAuthorityRecord, + resolve_weight_variance_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +AUTHORITY_REFERENCE = "variance_compatibility_authority:11111111-1111-4111-8111-111111111111" +SAMPLING_REFERENCE = "sampling_design_receipt:22222222-2222-4222-8222-222222222222" +VARIANCE_REFERENCE = "variance_design_receipt:33333333-3333-4333-8333-333333333333" +METHOD_REFERENCE = "variance_method:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +SAMPLING_DIGEST = "1" * 64 +ANALYSIS_WEIGHT_DIGEST = "2" * 64 +CASE_SET_DIGEST = "3" * 64 +ELIGIBILITY_DIGEST = "4" * 64 +FINAL_WEIGHT_DIGEST = "6" * 64 +VARIANCE_DIGEST = "7" * 64 +OWNER_DIGEST = "8" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +SUPERSEDED_AT = datetime(2026, 9, 17, 3, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "authority_reference", + "sampling_receipt_reference", + "sampling_receipt_version", + "sampling_receipt_digest", + "analysis_weight_receipt_digest", + "analytic_case_occurrence_set_digest", + "weight_eligibility_receipt_digest", + "weight_correction_sequence", + "final_weight_artifact_digest", + "variance_design_receipt_reference", + "variance_design_receipt_version", + "variance_design_receipt_digest", + "variance_method_reference", + "variance_method_version", + "variance_evidence_mode", + "variance_semantics", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _ReadPort: + def __init__(self, record: WeightVarianceAuthorityRecord) -> None: + self.record = record + + def read_weight_variance_authority(self, **_: object) -> WeightVarianceAuthorityRecord: + return self.record + + +def _record(**overrides: object) -> WeightVarianceAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY_REFERENCE, + "sampling_receipt_reference": SAMPLING_REFERENCE, + "sampling_receipt_version": 3, + "sampling_receipt_digest": SAMPLING_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "analytic_case_occurrence_set_digest": CASE_SET_DIGEST, + "weight_eligibility_receipt_digest": ELIGIBILITY_DIGEST, + "weight_correction_sequence": 9, + "final_weight_artifact_digest": FINAL_WEIGHT_DIGEST, + "variance_design_receipt_reference": VARIANCE_REFERENCE, + "variance_design_receipt_version": 5, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "variance_method_reference": METHOD_REFERENCE, + "variance_method_version": 2, + "variance_evidence_mode": "replicate_weights", + "variance_semantics": "exact", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 4, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": SUPERSEDED_AT, + } + values.update(overrides) + return WeightVarianceAuthorityRecord(**values) + + +def _resolve(*, used_at: datetime) -> object: + return resolve_weight_variance_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + sampling_receipt_reference=SAMPLING_REFERENCE, + sampling_receipt_version=3, + sampling_receipt_digest=SAMPLING_DIGEST, + analysis_weight_receipt_digest=ANALYSIS_WEIGHT_DIGEST, + analytic_case_occurrence_set_digest=CASE_SET_DIGEST, + weight_eligibility_receipt_digest=ELIGIBILITY_DIGEST, + weight_correction_sequence=9, + final_weight_artifact_digest=FINAL_WEIGHT_DIGEST, + variance_design_receipt_reference=VARIANCE_REFERENCE, + variance_design_receipt_version=5, + variance_design_receipt_digest=VARIANCE_DIGEST, + variance_method_reference=METHOD_REFERENCE, + variance_method_version=2, + variance_evidence_mode="replicate_weights", + variance_semantics="exact", + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=4, + used_at=used_at, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-variance-authority-read-v2", + resource_kind="weight_variance_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(_record()), + ) + + +def test_chronology_is_owner_evidence_not_caller_input() -> None: + parameters = signature(resolve_weight_variance_authority).parameters + assert "owner_contract_released_at" not in parameters + assert "superseded_at" not in parameters + + +def test_owner_contract_cannot_retroactively_authorize_compatibility() -> None: + with pytest.raises(ValueError, match="owner contract"): + _record( + owner_contract_released_at=datetime( + 2026, 9, 17, 1, 1, tzinfo=timezone.utc + ) + ) + + +def test_compatibility_uses_owner_resolved_half_open_authority_interval() -> None: + historical = _resolve(used_at=datetime(2026, 9, 17, 2, 59, tzinfo=timezone.utc)) + fields = dict(historical.fields) + assert fields["owner_contract_released_at"] == OWNER_RELEASED_AT + assert fields["superseded_at"] == SUPERSEDED_AT + + with pytest.raises(WeightVarianceAuthorityIntegrityError, match="supersession"): + _resolve(used_at=SUPERSEDED_AT) diff --git a/services/workforce-validation-api/tests/test_weight_variance_authority_edges.py b/services/workforce-validation-api/tests/test_weight_variance_authority_edges.py new file mode 100644 index 000000000..c36d7c6d9 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_authority_edges.py @@ -0,0 +1,309 @@ +"""Branch and hostile-input coverage for weight/variance authority resolution.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityIntegrityError, + WeightVarianceAuthorityReadPort, + WeightVarianceAuthorityRecord, + resolve_weight_variance_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +AUTHORITY_REFERENCE = "variance_compatibility_authority:11111111-1111-4111-8111-111111111111" +SAMPLING_REFERENCE = "sampling_design_receipt:22222222-2222-4222-8222-222222222222" +VARIANCE_REFERENCE = "variance_design_receipt:33333333-3333-4333-8333-333333333333" +METHOD_REFERENCE = "variance_method:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +SAMPLING_DIGEST = "1" * 64 +ANALYSIS_WEIGHT_DIGEST = "2" * 64 +CASE_SET_DIGEST = "3" * 64 +ELIGIBILITY_DIGEST = "4" * 64 +CORRECTION_SEQUENCE = 9 +FINAL_WEIGHT_DIGEST = "6" * 64 +VARIANCE_DIGEST = "7" * 64 +OWNER_DIGEST = "8" * 64 +OWNER_RELEASED_AT = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED_AT = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 2, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "authority_reference", + "sampling_receipt_reference", + "sampling_receipt_version", + "sampling_receipt_digest", + "analysis_weight_receipt_digest", + "analytic_case_occurrence_set_digest", + "weight_eligibility_receipt_digest", + "weight_correction_sequence", + "final_weight_artifact_digest", + "variance_design_receipt_reference", + "variance_design_receipt_version", + "variance_design_receipt_digest", + "variance_method_reference", + "variance_method_version", + "variance_evidence_mode", + "variance_semantics", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } +) + + +class _Port: + def __init__(self, result: object) -> None: + self.result = result + self.calls = 0 + + def read_weight_variance_authority(self, **_: object) -> object: + self.calls += 1 + return self.result + + +class _NoMethod: + pass + + +class _ProtocolOnly(WeightVarianceAuthorityReadPort): + pass + + +class _Descriptor: + @property + def read_weight_variance_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy() -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-variance-authority-read-v2", + resource_kind="weight_variance_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> WeightVarianceAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY_REFERENCE, + "sampling_receipt_reference": SAMPLING_REFERENCE, + "sampling_receipt_version": 3, + "sampling_receipt_digest": SAMPLING_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "analytic_case_occurrence_set_digest": CASE_SET_DIGEST, + "weight_eligibility_receipt_digest": ELIGIBILITY_DIGEST, + "weight_correction_sequence": CORRECTION_SEQUENCE, + "final_weight_artifact_digest": FINAL_WEIGHT_DIGEST, + "variance_design_receipt_reference": VARIANCE_REFERENCE, + "variance_design_receipt_version": 5, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "variance_method_reference": METHOD_REFERENCE, + "variance_method_version": 2, + "variance_evidence_mode": "replicate_weights", + "variance_semantics": "exact", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 4, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED_AT, + "released_at": RELEASED_AT, + "superseded_at": None, + } + values.update(overrides) + return WeightVarianceAuthorityRecord(**values) + + +def _resolve(*, result: object | None = None, read_port: object | None = None, **overrides: object): + port = _Port(_record() if result is None else result) if read_port is None else read_port + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "sampling_receipt_reference": SAMPLING_REFERENCE, + "sampling_receipt_version": 3, + "sampling_receipt_digest": SAMPLING_DIGEST, + "analysis_weight_receipt_digest": ANALYSIS_WEIGHT_DIGEST, + "analytic_case_occurrence_set_digest": CASE_SET_DIGEST, + "weight_eligibility_receipt_digest": ELIGIBILITY_DIGEST, + "weight_correction_sequence": CORRECTION_SEQUENCE, + "final_weight_artifact_digest": FINAL_WEIGHT_DIGEST, + "variance_design_receipt_reference": VARIANCE_REFERENCE, + "variance_design_receipt_version": 5, + "variance_design_receipt_digest": VARIANCE_DIGEST, + "variance_method_reference": METHOD_REFERENCE, + "variance_method_version": 2, + "variance_evidence_mode": "replicate_weights", + "variance_semantics": "exact", + "owner_contract_reference": OWNER_REFERENCE, + "owner_contract_version": 4, + "used_at": USED_AT, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": port, + } + values.update(overrides) + return resolve_weight_variance_authority(**values) + + +@pytest.mark.parametrize( + ("key", "value"), + [ + ("tenant_record_id", "not-a-uuid"), + ("validity_study_id", UUID(int=0)), + ("authority_reference", 42), + ("authority_reference", "not-a-reference"), + ("authority_reference", "wrong:authority"), + ("sampling_receipt_reference", "wrong:sampling"), + ("sampling_receipt_version", True), + ("sampling_receipt_version", 0), + ("sampling_receipt_digest", 42), + ("sampling_receipt_digest", "1" * 63), + ("analysis_weight_receipt_digest", "2" * 63), + ("analytic_case_occurrence_set_digest", "3" * 65), + ("weight_eligibility_receipt_digest", "4" * 63), + ("weight_correction_sequence", True), + ("weight_correction_sequence", 0), + ("final_weight_artifact_digest", "6" * 63), + ("variance_design_receipt_reference", "wrong:variance"), + ("variance_design_receipt_version", 0), + ("variance_design_receipt_digest", "7" * 63), + ("variance_method_reference", "wrong:method"), + ("variance_method_version", False), + ("variance_evidence_mode", 42), + ("variance_evidence_mode", "unknown"), + ("variance_semantics", 42), + ("variance_semantics", "unknown"), + ("owner_contract_reference", "wrong:owner"), + ("owner_contract_version", 0), + ("owner_contract_digest", "8" * 63), + ("owner_contract_released_at", datetime(2026, 9, 17, 0, 30)), + ("released_at", datetime(2026, 9, 17, 1, 0)), + ("superseded_at", datetime(2026, 9, 17, 3, 0)), + ("superseded_at", RELEASED_AT), + ], +) +def test_record_rejects_malformed_authority_evidence(key: str, value: object) -> None: + with pytest.raises(ValueError): + _record(**{key: value}) + + +def test_approximation_mode_accepts_only_explicit_approximate_semantics() -> None: + record = _record(variance_evidence_mode="approximation", variance_semantics="approximate") + assert record.variance_evidence_mode == "approximation" + assert record.variance_semantics == "approximate" + with pytest.raises(ValueError, match="approximation evidence"): + _record(variance_evidence_mode="approximation", variance_semantics="exact") + with pytest.raises(ValueError, match="approximation evidence"): + _resolve(variance_evidence_mode="approximation", variance_semantics="exact") + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("sampling_receipt_reference", "wrong:sampling", ValueError), + ("sampling_receipt_version", True, ValueError), + ("sampling_receipt_digest", "1" * 63, ValueError), + ("analysis_weight_receipt_digest", "2" * 63, ValueError), + ("analytic_case_occurrence_set_digest", "3" * 63, ValueError), + ("weight_eligibility_receipt_digest", "4" * 63, ValueError), + ("weight_correction_sequence", 0, ValueError), + ("final_weight_artifact_digest", "6" * 63, ValueError), + ("variance_design_receipt_reference", "wrong:variance", ValueError), + ("variance_design_receipt_version", 0, ValueError), + ("variance_design_receipt_digest", "7" * 63, ValueError), + ("variance_method_reference", "wrong:method", ValueError), + ("variance_method_version", 0, ValueError), + ("variance_evidence_mode", "unknown", ValueError), + ("variance_semantics", "unknown", ValueError), + ("owner_contract_reference", "wrong:owner", ValueError), + ("owner_contract_version", False, ValueError), + ("used_at", datetime(2026, 9, 17, 2, 0), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port = _Port(_record()) + with pytest.raises(error): + _resolve(read_port=port, **{key: value}) + assert port.calls == 0 + + +@pytest.mark.parametrize("read_port", [_NoMethod(), _ProtocolOnly(), _Descriptor()]) +def test_nonconcrete_owner_capabilities_are_rejected_without_execution(read_port: object) -> None: + with pytest.raises(TypeError): + _resolve(read_port=read_port) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"sampling_receipt_reference": "sampling_design_receipt:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"}, + {"sampling_receipt_version": 4}, + {"sampling_receipt_digest": "a" * 64}, + {"analysis_weight_receipt_digest": "b" * 64}, + {"analytic_case_occurrence_set_digest": "c" * 64}, + {"weight_eligibility_receipt_digest": "d" * 64}, + {"weight_correction_sequence": 10}, + {"final_weight_artifact_digest": "f" * 64}, + {"variance_design_receipt_reference": "variance_design_receipt:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"}, + {"variance_design_receipt_version": 6}, + {"variance_design_receipt_digest": "a" * 64}, + {"variance_method_reference": "variance_method:cccccccc-cccc-4ccc-8ccc-cccccccccccc"}, + {"variance_method_version": 3}, + {"variance_evidence_mode": "joint_inclusion"}, + {"variance_semantics": "approximate"}, + {"owner_contract_reference": "released_owner_contract:dddddddd-dddd-4ddd-8ddd-dddddddddddd"}, + {"owner_contract_version": 5}, + ], +) +def test_every_requested_coordinate_must_match_owner_evidence( + record_overrides: dict[str, object] +) -> None: + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve(result=_record(**record_overrides)) + + +def test_uuid_views_are_detached_from_retained_references() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + view = _resolve(result=record) + returned = view.tenant_record_id + object.__setattr__(returned, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT diff --git a/services/workforce-validation-api/tests/test_weight_variance_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_weight_variance_authority_structural_integrity.py new file mode 100644 index 000000000..9cecc82de --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_authority_structural_integrity.py @@ -0,0 +1,33 @@ +"""Structural-integrity regressions for point-weight/variance owner evidence.""" + +from __future__ import annotations + +import pytest + +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityIntegrityError, + WeightVarianceAuthorityRecord, +) +from test_weight_variance_authority import _ReadPort, _record, _resolve + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + WeightVarianceAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + WeightVarianceAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(WeightVarianceAuthorityIntegrityError): + _resolve(read_port=_ReadPort(forged)) diff --git a/services/workforce-validation-api/tests/test_weight_variance_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_weight_variance_authority_view_issuance_integrity.py new file mode 100644 index 000000000..6d61fc55d --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_authority_view_issuance_integrity.py @@ -0,0 +1,57 @@ +"""Regression contract for point-weight/variance authority view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityIntegrityError, + WeightVarianceAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f2") + + +def test_low_level_tuple_construction_cannot_issue_weight_variance_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + WeightVarianceAuthorityView, + (TENANT.int, STUDY.int, (("authority_reference", "x"),)), + ) + + +def test_unsealed_object_allocation_cannot_expose_weight_variance_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(WeightVarianceAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + WeightVarianceAuthorityIntegrityError, + match="was not issued by resolve_weight_variance_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_weight_variance_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(WeightVarianceAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + WeightVarianceAuthorityIntegrityError, + match="was not issued by resolve_weight_variance_authority", + ): + _ = forged_view.fields + + +def test_raw_weight_variance_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(WeightVarianceAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_weight_variance_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_weight_variance_authority_view_seal_capability.py new file mode 100644 index 000000000..0ebb7532e --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_authority_view_seal_capability.py @@ -0,0 +1,44 @@ +"""Hostile sealing-capability regression for weight/variance authority views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.variance_authority as authority_module +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityIntegrityError, + WeightVarianceAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000437") +STUDY = UUID("00000000-0000-0000-0000-000000000438") + + +def _raw_view_with_module_marker() -> WeightVarianceAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(WeightVarianceAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__(view, "_fields", (("variance_design_receipt_digest", "b" * 64),)) + object.__setattr__( + view, + "_issuance_marker", + getattr(authority_module, "_WEIGHT_VARIANCE_VIEW_ISSUANCE_MARKER", object()), + ) + return view + + +def test_module_exposes_no_weight_variance_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr(authority_module, "_WEIGHT_VARIANCE_VIEW_ISSUANCE_MARKER") + + +def test_importable_marker_cannot_mint_weight_variance_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + WeightVarianceAuthorityIntegrityError, + match="weight variance view was not issued by resolve_weight_variance_authority", + ): + _ = forged_view.fields diff --git a/services/workforce-validation-api/tests/test_weight_variance_read_port_key_completeness.py b/services/workforce-validation-api/tests/test_weight_variance_read_port_key_completeness.py new file mode 100644 index 000000000..78545d5a4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_read_port_key_completeness.py @@ -0,0 +1,162 @@ +"""Require the owner read to key the complete point-weight/variance compatibility tuple.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.variance_authority import ( + WeightVarianceAuthorityRecord, + resolve_weight_variance_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +SAMPLING_REFERENCE = "sampling_design_receipt:22222222-2222-4222-8222-222222222222" +VARIANCE_REFERENCE = "variance_design_receipt:33333333-3333-4333-8333-333333333333" +METHOD_REFERENCE = "variance_method:44444444-4444-4444-8444-444444444444" +OWNER_REFERENCE = "released_owner_contract:55555555-5555-4555-8555-555555555555" +AUTHORITY_REFERENCE = "variance_compatibility_authority:11111111-1111-4111-8111-111111111111" +SAMPLING_DIGEST = "1" * 64 +ANALYSIS_WEIGHT_DIGEST = "2" * 64 +CASE_SET_DIGEST = "3" * 64 +ELIGIBILITY_DIGEST = "4" * 64 +FINAL_WEIGHT_DIGEST = "6" * 64 +VARIANCE_DIGEST = "7" * 64 +OWNER_DIGEST = "8" * 64 +RELEASED_AT = datetime(2026, 9, 17, 1, tzinfo=timezone.utc) +USED_AT = datetime(2026, 9, 17, 2, tzinfo=timezone.utc) + + +class _StrictReadPort: + """Require every coordinate needed to select one compatibility record.""" + + def read_weight_variance_authority( + self, + *, + tenant_record_id: UUID, + validity_study_id: UUID, + sampling_receipt_reference: str, + sampling_receipt_version: int, + sampling_receipt_digest: str, + analysis_weight_receipt_digest: str, + analytic_case_occurrence_set_digest: str, + weight_eligibility_receipt_digest: str, + weight_correction_sequence: int, + final_weight_artifact_digest: str, + variance_design_receipt_reference: str, + variance_design_receipt_version: int, + variance_design_receipt_digest: str, + variance_method_reference: str, + variance_method_version: int, + variance_evidence_mode: str, + variance_semantics: str, + owner_contract_reference: str, + owner_contract_version: int, + ) -> WeightVarianceAuthorityRecord: + assert analytic_case_occurrence_set_digest == CASE_SET_DIGEST + assert weight_eligibility_receipt_digest == ELIGIBILITY_DIGEST + assert weight_correction_sequence == 9 + assert final_weight_artifact_digest == FINAL_WEIGHT_DIGEST + assert variance_method_reference == METHOD_REFERENCE + assert variance_method_version == 2 + assert variance_evidence_mode == "replicate_weights" + assert variance_semantics == "exact" + return WeightVarianceAuthorityRecord( + tenant_record_id=tenant_record_id, + validity_study_id=validity_study_id, + authority_reference=AUTHORITY_REFERENCE, + sampling_receipt_reference=sampling_receipt_reference, + sampling_receipt_version=sampling_receipt_version, + sampling_receipt_digest=sampling_receipt_digest, + analysis_weight_receipt_digest=analysis_weight_receipt_digest, + analytic_case_occurrence_set_digest=analytic_case_occurrence_set_digest, + weight_eligibility_receipt_digest=weight_eligibility_receipt_digest, + weight_correction_sequence=weight_correction_sequence, + final_weight_artifact_digest=final_weight_artifact_digest, + variance_design_receipt_reference=variance_design_receipt_reference, + variance_design_receipt_version=variance_design_receipt_version, + variance_design_receipt_digest=variance_design_receipt_digest, + variance_method_reference=variance_method_reference, + variance_method_version=variance_method_version, + variance_evidence_mode=variance_evidence_mode, + variance_semantics=variance_semantics, + owner_contract_reference=owner_contract_reference, + owner_contract_version=owner_contract_version, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=RELEASED_AT, + released_at=RELEASED_AT, + ) + + +def test_owner_read_receives_complete_compatibility_tuple() -> None: + principal = ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + policy = PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-variance-authority-read-v2", + resource_kind="weight_variance_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=frozenset( + { + "authority_reference", + "sampling_receipt_reference", + "sampling_receipt_version", + "sampling_receipt_digest", + "analysis_weight_receipt_digest", + "analytic_case_occurrence_set_digest", + "weight_eligibility_receipt_digest", + "weight_correction_sequence", + "final_weight_artifact_digest", + "variance_design_receipt_reference", + "variance_design_receipt_version", + "variance_design_receipt_digest", + "variance_method_reference", + "variance_method_version", + "variance_evidence_mode", + "variance_semantics", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + } + ), + ) + + view = resolve_weight_variance_authority( + principal=principal, + tenant_record_id=TENANT, + validity_study_id=STUDY, + sampling_receipt_reference=SAMPLING_REFERENCE, + sampling_receipt_version=3, + sampling_receipt_digest=SAMPLING_DIGEST, + analysis_weight_receipt_digest=ANALYSIS_WEIGHT_DIGEST, + analytic_case_occurrence_set_digest=CASE_SET_DIGEST, + weight_eligibility_receipt_digest=ELIGIBILITY_DIGEST, + weight_correction_sequence=9, + final_weight_artifact_digest=FINAL_WEIGHT_DIGEST, + variance_design_receipt_reference=VARIANCE_REFERENCE, + variance_design_receipt_version=5, + variance_design_receipt_digest=VARIANCE_DIGEST, + variance_method_reference=METHOD_REFERENCE, + variance_method_version=2, + variance_evidence_mode="replicate_weights", + variance_semantics="exact", + owner_contract_reference=OWNER_REFERENCE, + owner_contract_version=4, + used_at=USED_AT, + purpose_code="selection_validity_analysis", + policy=policy, + read_port=_StrictReadPort(), + ) + + assert dict(view.fields)["variance_method_reference"] == METHOD_REFERENCE diff --git a/services/workforce-validation-api/tests/test_weight_variance_supersession_authority.py b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority.py new file mode 100644 index 000000000..e4ffa29ca --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority.py @@ -0,0 +1,292 @@ +"""Append-only correction contract for point-weight/variance compatibility authority.""" + +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from uuid import UUID + +import pytest + +from orgmetra_keyverse_adapter import AuthorizationDeniedError, PurposeBoundAccessPolicy +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_variance_supersession_authority import ( + WeightVarianceSupersessionAuthorityIntegrityError, + WeightVarianceSupersessionAuthorityNotFound, + WeightVarianceSupersessionAuthorityReadPort, + WeightVarianceSupersessionAuthorityRecord, + WeightVarianceSupersessionAuthorityView, + resolve_weight_variance_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +OTHER_TENANT = UUID("10000000-0000-7000-8000-000000000002") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +OTHER_STUDY = UUID("00000000-0000-7000-8000-0000000000d2") +AUTHORITY = "variance_compatibility_authority:11111111-1111-4111-8111-111111111111" +SUCCESSOR = "variance_compatibility_authority:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 1, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "authority_reference", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_authority_reference", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + def __init__(self, result: object) -> None: + self.result = result + self.calls: list[dict[str, object]] = [] + + def read_weight_variance_supersession_authority(self, **coordinates: object) -> object: + self.calls.append(dict(coordinates)) + return self.result + + +class _ProtocolOnly(WeightVarianceSupersessionAuthorityReadPort): + pass + + +class _DescriptorReadPort: + @property + def read_weight_variance_supersession_authority(self) -> object: + raise AssertionError("descriptor must not execute") + + +class _NoReadMethod: + pass + + +def _principal() -> ValidationPrincipal: + return ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ) + + +def _policy(*, purpose_code: str = "selection_validity_analysis") -> PurposeBoundAccessPolicy: + return PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-variance-supersession-read-v1", + resource_kind="weight_variance_supersession_authority", + purpose_code=purpose_code, + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ) + + +def _record(**overrides: object) -> WeightVarianceSupersessionAuthorityRecord: + values: dict[str, object] = { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "owner_contract_released_at": OWNER_RELEASED, + "released_at": RELEASED, + "superseded_at": CUTOVER, + "successor_authority_reference": SUCCESSOR, + "successor_evidence_version": 1, + "successor_released_at": CUTOVER, + } + values.update(overrides) + return WeightVarianceSupersessionAuthorityRecord(**values) + + +def _resolve(*, read_port: object, used_at: datetime, **overrides: object): + values: dict[str, object] = { + "principal": _principal(), + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + "used_at": used_at, + "purpose_code": "selection_validity_analysis", + "policy": _policy(), + "read_port": read_port, + } + values.update(overrides) + return resolve_weight_variance_supersession_authority(**values) + + +def test_successor_edge_requires_complete_atomic_released_coordinates() -> None: + with pytest.raises(ValueError, match="evidence_version must remain 1"): + _record(evidence_version=2) + with pytest.raises(ValueError, match="complete released successor coordinates"): + _record(successor_released_at=None) + with pytest.raises(ValueError, match="later than compatibility authority release"): + _record(superseded_at=RELEASED) + with pytest.raises(ValueError, match="new reference"): + _record(successor_authority_reference=AUTHORITY) + with pytest.raises(ValueError, match="successor_evidence_version must remain 1"): + _record(successor_evidence_version=2) + with pytest.raises(ValueError, match="released after its predecessor"): + _record(superseded_at=RELEASED + timedelta(seconds=1), successor_released_at=RELEASED) + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=CUTOVER - timedelta(seconds=1)) + with pytest.raises(ValueError, match="exactly at supersession"): + _record(successor_released_at=CUTOVER + timedelta(seconds=1)) + + +def test_chronology_requires_released_owner_and_timezone_aware_instants() -> None: + with pytest.raises(ValueError, match="owner contract"): + _record(owner_contract_released_at=RELEASED + timedelta(seconds=1)) + with pytest.raises(ValueError): + _record(owner_contract_released_at=datetime(2026, 9, 17, 0, 30)) + with pytest.raises(ValueError): + _record(released_at=datetime(2026, 9, 17, 1, 0)) + with pytest.raises(ValueError): + _record(superseded_at=datetime(2026, 9, 18, 1, 0)) + with pytest.raises(ValueError): + _record(successor_released_at=datetime(2026, 9, 18, 1, 0)) + + +def test_historical_use_is_allowed_but_cutover_use_fails_closed() -> None: + record = _record() + port = _ReadPort(record) + view = _resolve(read_port=port, used_at=CUTOVER - timedelta(microseconds=1)) + + assert view.validity_study_id == STUDY + + assert isinstance(port, WeightVarianceSupersessionAuthorityReadPort) + assert port.calls == [ + { + "tenant_record_id": TENANT, + "validity_study_id": STUDY, + "authority_reference": AUTHORITY, + "evidence_version": 1, + "owner_contract_reference": OWNER, + "owner_contract_version": 1, + "owner_contract_digest": OWNER_DIGEST, + } + ] + assert ("authority_reference", AUTHORITY) in view.fields + assert ("released_at", RELEASED) in view.fields + assert ("superseded_at", CUTOVER) in view.fields + assert all(not name.startswith("successor_") for name, _ in view.fields) + + with pytest.raises(WeightVarianceSupersessionAuthorityIntegrityError, match="superseded"): + _resolve(read_port=_ReadPort(record), used_at=CUTOVER) + + +def test_open_interval_without_successor_remains_current() -> None: + record = _record( + superseded_at=None, + successor_authority_reference=None, + successor_evidence_version=None, + successor_released_at=None, + ) + view = _resolve(read_port=_ReadPort(record), used_at=CUTOVER + timedelta(days=30)) + assert ("superseded_at", None) in view.fields + + +def test_missing_noncanonical_and_pre_release_evidence_fail_closed() -> None: + with pytest.raises(WeightVarianceSupersessionAuthorityNotFound): + _resolve(read_port=_ReadPort(None), used_at=RELEASED) + with pytest.raises(WeightVarianceSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(object()), used_at=RELEASED) + with pytest.raises(WeightVarianceSupersessionAuthorityIntegrityError, match="released before scientific use"): + _resolve(read_port=_ReadPort(_record()), used_at=RELEASED - timedelta(seconds=1)) + + +@pytest.mark.parametrize( + "record_overrides", + [ + {"tenant_record_id": OTHER_TENANT}, + {"validity_study_id": OTHER_STUDY}, + {"authority_reference": SUCCESSOR}, + {"owner_contract_reference": "released_owner_contract:44444444-4444-4444-8444-444444444444"}, + {"owner_contract_version": 2}, + {"owner_contract_digest": "5" * 64}, + ], +) +def test_owner_evidence_must_match_every_requested_coordinate(record_overrides: dict[str, object]) -> None: + if "authority_reference" in record_overrides: + record_overrides = { + **record_overrides, + "successor_authority_reference": AUTHORITY, + } + with pytest.raises(WeightVarianceSupersessionAuthorityIntegrityError): + _resolve(read_port=_ReadPort(_record(**record_overrides)), used_at=RELEASED) + + +def test_authorization_denial_precedes_owner_read() -> None: + port = _ReadPort(_record()) + with pytest.raises(AuthorizationDeniedError): + _resolve(read_port=port, used_at=RELEASED, policy=_policy(purpose_code="audit_review")) + assert port.calls == [] + + +@pytest.mark.parametrize( + ("key", "value", "error"), + [ + ("principal", object(), TypeError), + ("policy", object(), TypeError), + ("read_port", _NoReadMethod(), TypeError), + ("read_port", _ProtocolOnly(), TypeError), + ("read_port", _DescriptorReadPort(), TypeError), + ("tenant_record_id", "not-a-uuid", ValueError), + ("validity_study_id", UUID(int=0), ValueError), + ("authority_reference", "wrong:authority", ValueError), + ("evidence_version", 2, ValueError), + ("owner_contract_reference", "wrong:contract", ValueError), + ("owner_contract_version", 0, ValueError), + ("owner_contract_digest", "3" * 63, ValueError), + ("used_at", datetime(2026, 9, 18, 0, 30), ValueError), + ("purpose_code", "Selection Validity Analysis", ValueError), + ], +) +def test_invalid_request_or_dependency_fails_before_owner_resolution( + key: str, value: object, error: type[Exception] +) -> None: + port: object = _ReadPort(_record()) + overrides = {key: value} + if key == "read_port": + port = value + overrides = {} + with pytest.raises(error): + _resolve(read_port=port, **{"used_at": RELEASED, **overrides}) + if isinstance(port, _ReadPort): + assert port.calls == [] + + +def test_record_and_view_are_immutable_and_uuid_views_detached() -> None: + tenant = UUID(str(TENANT)) + record = _record(tenant_record_id=tenant) + object.__setattr__(tenant, "int", OTHER_TENANT.int) + assert record.tenant_record_id == TENANT + + with pytest.raises(AttributeError): + object.__setattr__(record, "released_at", CUTOVER) + + view = _resolve(read_port=_ReadPort(record), used_at=RELEASED) + returned_tenant = view.tenant_record_id + object.__setattr__(returned_tenant, "int", OTHER_TENANT.int) + assert view.tenant_record_id == TENANT + + with pytest.raises(TypeError): + WeightVarianceSupersessionAuthorityView( + tenant_record_id=TENANT, + validity_study_id=STUDY, + fields=(), + ) diff --git a/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_structural_integrity.py b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_structural_integrity.py new file mode 100644 index 000000000..9d59e40b0 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_structural_integrity.py @@ -0,0 +1,131 @@ +"""Structural-integrity regressions for weight/variance supersession evidence.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from uuid import UUID + +import pytest +from orgmetra_keyverse_adapter import PurposeBoundAccessPolicy + +from orgmetra_workforce_validation_api import ValidationPrincipal +from orgmetra_workforce_validation_api.weight_variance_supersession_authority import ( + WeightVarianceSupersessionAuthorityIntegrityError, + WeightVarianceSupersessionAuthorityRecord, + resolve_weight_variance_supersession_authority, +) + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000d1") +AUTHORITY = "variance_compatibility_authority:11111111-1111-4111-8111-111111111111" +SUCCESSOR = "variance_compatibility_authority:22222222-2222-4222-8222-222222222222" +OWNER = "released_owner_contract:33333333-3333-4333-8333-333333333333" +OWNER_DIGEST = "3" * 64 +OWNER_RELEASED = datetime(2026, 9, 17, 0, 30, tzinfo=timezone.utc) +RELEASED = datetime(2026, 9, 17, 1, 0, tzinfo=timezone.utc) +CUTOVER = datetime(2026, 9, 18, 1, 0, tzinfo=timezone.utc) +READ_FIELDS = frozenset( + { + "authority_reference", + "evidence_version", + "owner_contract_reference", + "owner_contract_version", + "owner_contract_digest", + "owner_contract_released_at", + "released_at", + "superseded_at", + "successor_authority_reference", + "successor_evidence_version", + "successor_released_at", + } +) + + +class _ReadPort: + """Return configured persisted evidence.""" + + def __init__(self, result: object) -> None: + self.result = result + + def read_weight_variance_supersession_authority(self, **_: object) -> object: + """Return the configured owner evidence.""" + return self.result + + +def _record() -> WeightVarianceSupersessionAuthorityRecord: + return WeightVarianceSupersessionAuthorityRecord( + tenant_record_id=TENANT, + validity_study_id=STUDY, + authority_reference=AUTHORITY, + evidence_version=1, + owner_contract_reference=OWNER, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + owner_contract_released_at=OWNER_RELEASED, + released_at=RELEASED, + superseded_at=CUTOVER, + successor_authority_reference=SUCCESSOR, + successor_evidence_version=1, + successor_released_at=CUTOVER, + ) + + +def _resolve(result: object) -> object: + return resolve_weight_variance_supersession_authority( + principal=ValidationPrincipal( + tenant_record_id=TENANT, + actor_reference="person:validation-analyst-1", + granted_scope_codes=frozenset({"orgmetra.workforce_validation.read"}), + ), + tenant_record_id=TENANT, + validity_study_id=STUDY, + authority_reference=AUTHORITY, + evidence_version=1, + owner_contract_reference=OWNER, + owner_contract_version=1, + owner_contract_digest=OWNER_DIGEST, + used_at=RELEASED, + purpose_code="selection_validity_analysis", + policy=PurposeBoundAccessPolicy( + tenant_record_id=TENANT, + policy_version_code="weight-variance-supersession-read-v1", + resource_kind="weight_variance_supersession_authority", + purpose_code="selection_validity_analysis", + operation_code="read", + required_scope_code="orgmetra.workforce_validation.read", + permitted_fields=READ_FIELDS, + ), + read_port=_ReadPort(result), + ) + + +def test_hidden_trailing_tuple_structure_fails_closed() -> None: + canonical = _record() + forged = tuple.__new__( + WeightVarianceSupersessionAuthorityRecord, + tuple(canonical) + ("hidden-owner-coordinate",), + ) + + with pytest.raises(WeightVarianceSupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_truncated_exact_typed_tuple_maps_to_integrity_error() -> None: + canonical = _record() + forged = tuple.__new__( + WeightVarianceSupersessionAuthorityRecord, + tuple(canonical)[:-1], + ) + + with pytest.raises(WeightVarianceSupersessionAuthorityIntegrityError): + _resolve(forged) + + +def test_duplicate_nested_current_field_cannot_be_normalized_away() -> None: + canonical = _record() + raw = list(canonical) + raw[2] = canonical.fields + (("authority_reference", AUTHORITY),) + forged = tuple.__new__(WeightVarianceSupersessionAuthorityRecord, tuple(raw)) + + with pytest.raises(WeightVarianceSupersessionAuthorityIntegrityError): + _resolve(forged) diff --git a/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_view_issuance_integrity.py b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_view_issuance_integrity.py new file mode 100644 index 000000000..8891b0da4 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_view_issuance_integrity.py @@ -0,0 +1,61 @@ +"""Regression contract for weight/variance supersession view issuance integrity.""" + +from uuid import UUID + +import pytest + +from orgmetra_workforce_validation_api.weight_variance_supersession_authority import ( + WeightVarianceSupersessionAuthorityIntegrityError, + WeightVarianceSupersessionAuthorityView, +) + + +TENANT = UUID("10000000-0000-7000-8000-000000000001") +STUDY = UUID("00000000-0000-7000-8000-0000000000f1") + + +def test_low_level_tuple_construction_cannot_issue_supersession_view() -> None: + """Remove tuple's base constructor as an alternate authorized-view issuer.""" + with pytest.raises(TypeError): + tuple.__new__( + WeightVarianceSupersessionAuthorityView, + ( + TENANT.int, + STUDY.int, + (("authority_reference", "variance_compatibility_authority:test"),), + ), + ) + + +def test_unsealed_object_allocation_cannot_expose_supersession_view() -> None: + """Require the resolver seal before raw exact-runtime objects expose state.""" + unsealed = object.__new__(WeightVarianceSupersessionAuthorityView) + + for attribute_name in ("tenant_record_id", "validity_study_id", "fields"): + with pytest.raises( + WeightVarianceSupersessionAuthorityIntegrityError, + match="was not issued by resolve_weight_variance_supersession_authority", + ): + getattr(unsealed, attribute_name) + + +def test_wrong_issuance_marker_cannot_expose_supersession_view() -> None: + """Reject marker-shaped objects that did not originate from the resolver.""" + forged_view = object.__new__(WeightVarianceSupersessionAuthorityView) + object.__setattr__(forged_view, "_issuance_marker", object()) + + with pytest.raises( + WeightVarianceSupersessionAuthorityIntegrityError, + match="was not issued by resolve_weight_variance_supersession_authority", + ): + _ = forged_view.fields + + +def test_raw_supersession_view_rejects_mutation_and_deletion() -> None: + """Keep projection state immutable after raw exact-runtime allocation.""" + raw_view = object.__new__(WeightVarianceSupersessionAuthorityView) + + with pytest.raises(AttributeError, match="immutable"): + raw_view._fields = () + with pytest.raises(AttributeError, match="immutable"): + del raw_view._fields diff --git a/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_view_seal_capability.py b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_view_seal_capability.py new file mode 100644 index 000000000..eb3e57ab0 --- /dev/null +++ b/services/workforce-validation-api/tests/test_weight_variance_supersession_authority_view_seal_capability.py @@ -0,0 +1,54 @@ +"""Hostile sealing-capability regression for weight/variance supersession views.""" + +from uuid import UUID + +import pytest + +import orgmetra_workforce_validation_api.weight_variance_supersession_authority as authority_module +from orgmetra_workforce_validation_api.weight_variance_supersession_authority import ( + WeightVarianceSupersessionAuthorityIntegrityError, + WeightVarianceSupersessionAuthorityView, +) + +TENANT = UUID("00000000-0000-0000-0000-000000000439") +STUDY = UUID("00000000-0000-0000-0000-000000000440") + + +def _raw_view_with_module_marker() -> WeightVarianceSupersessionAuthorityView: + """Build the strongest caller-owned exact-runtime forgery available from module state.""" + view = object.__new__(WeightVarianceSupersessionAuthorityView) + object.__setattr__(view, "_tenant_identity", TENANT.int) + object.__setattr__(view, "_study_identity", STUDY.int) + object.__setattr__(view, "_fields", (("authority_reference", "variance_compatibility_authority:x"),)) + object.__setattr__( + view, + "_issuance_marker", + getattr( + authority_module, + "_WEIGHT_VARIANCE_SUPERSESSION_VIEW_ISSUANCE_MARKER", + object(), + ), + ) + return view + + +def test_module_exposes_no_weight_variance_supersession_view_seal() -> None: + """Keep the write capability out of ordinary importable module state.""" + assert not hasattr( + authority_module, + "_WEIGHT_VARIANCE_SUPERSESSION_VIEW_ISSUANCE_MARKER", + ) + + +def test_importable_marker_cannot_mint_weight_variance_supersession_view() -> None: + """Require caller-populated exact objects to remain unreadable.""" + forged_view = _raw_view_with_module_marker() + + with pytest.raises( + WeightVarianceSupersessionAuthorityIntegrityError, + match=( + "weight/variance supersession view was not issued by " + "resolve_weight_variance_supersession_authority" + ), + ): + _ = forged_view.fields diff --git a/tests/test_foundation_ci_dependency_hygiene.sh b/tests/test_foundation_ci_dependency_hygiene.sh index 2c0f5087f..8a8fb6328 100644 --- a/tests/test_foundation_ci_dependency_hygiene.sh +++ b/tests/test_foundation_ci_dependency_hygiene.sh @@ -18,10 +18,11 @@ expected_pythonpaths=( "packages/selection-review/src" "services/job-analysis-api/src:packages/hris-kernel/src:packages/keyverse-adapter/src" "services/people-api/src:packages/hris-kernel/src:packages/keyverse-adapter/src" + "services/workforce-validation-api/src:packages/keyverse-adapter/src" ) if ! grep -Fq -- "${expected_install}" "${workflow_path}"; then - printf 'Foundation CI must install only the hash-locked test toolchain.\n' >&2 + printf 'Foundation CI must install only the hash-locked test/build toolchain.\n' >&2 exit 1 fi @@ -72,8 +73,8 @@ if [[ ! -f "${requirements_path}" ]]; then fi mapfile -t package_lines < <(grep -Ev '^[[:space:]]*(#|$)' "${requirements_path}") -if [[ "${#package_lines[@]}" -ne 7 ]]; then - printf 'Foundation CI requirements must contain the seven reviewed direct/runtime test packages.\n' >&2 +if [[ "${#package_lines[@]}" -ne 8 ]]; then + printf 'Foundation CI requirements must contain the eight reviewed direct/runtime test-build packages.\n' >&2 exit 1 fi @@ -84,7 +85,7 @@ for package_line in "${package_lines[@]}"; do fi done -for package_name in coverage iniconfig packaging pluggy Pygments pytest pytest-cov; do +for package_name in coverage iniconfig packaging pluggy Pygments pytest pytest-cov setuptools; do if ! printf '%s\n' "${package_lines[@]}" | grep -Eq "^${package_name}=="; then printf 'Foundation CI requirement is missing: %s\n' "${package_name}" >&2 exit 1 diff --git a/tests/test_workforce_validation_owner_schema_postgres.sh b/tests/test_workforce_validation_owner_schema_postgres.sh new file mode 100644 index 000000000..c79b659c8 --- /dev/null +++ b/tests/test_workforce_validation_owner_schema_postgres.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${DATABASE_URL:=postgresql://orgmetra:orgmetra@localhost:5432/orgmetra}" + +migration="services/workforce-validation-api/database/migrations/0001_owner_schema.sql" +psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -f "${migration}" + +role_flags="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SELECT rolcanlogin, rolsuper, rolcreatedb, rolcreaterole, rolinherit, rolreplication, rolbypassrls +FROM pg_roles +WHERE rolname = 'workforce_validation_role'; +")" +if [[ "${role_flags}" != "f|f|f|f|f|f|f" ]]; then + echo "workforce_validation_role flags are not deny-default: ${role_flags}" >&2 + exit 1 +fi + +schema_owner="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SELECT pg_get_userbyid(nspowner) +FROM pg_namespace +WHERE nspname = 'workforce_validation'; +")" +if [[ "${schema_owner}" != "workforce_validation_role" ]]; then + echo "workforce_validation schema has unexpected owner: ${schema_owner}" >&2 + exit 1 +fi + +role_config="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SELECT COALESCE(array_to_string(rolconfig, ','), '') +FROM pg_roles +WHERE rolname = 'workforce_validation_role'; +")" +if [[ -n "${role_config}" ]]; then + echo "NOLOGIN schema owner must not carry ineffective login-only runtime defaults: ${role_config}" >&2 + exit 1 +fi + +set_role_probe="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SET search_path = public; +SET ROLE workforce_validation_role; +SELECT current_user || '|' || current_setting('search_path'); +RESET ROLE; +")" +if [[ "${set_role_probe}" != "workforce_validation_role|public" ]]; then + echo "unexpected SET ROLE search_path behavior: ${set_role_probe}" >&2 + exit 1 +fi + +psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -qc "CREATE ROLE workforce_validation_public_probe NOLOGIN;" +trap 'psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -qc "DROP ROLE IF EXISTS workforce_validation_public_probe;" >/dev/null 2>&1 || true' EXIT + +public_usage="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SELECT has_schema_privilege('workforce_validation_public_probe', 'workforce_validation', 'USAGE'); +")" +public_create="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SELECT has_schema_privilege('workforce_validation_public_probe', 'workforce_validation', 'CREATE'); +")" +if [[ "${public_usage}" != "f" || "${public_create}" != "f" ]]; then + echo "PUBLIC retains workforce_validation schema privileges: usage=${public_usage} create=${public_create}" >&2 + exit 1 +fi + +relation_count="$(psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -Atqc " +SELECT count(*) +FROM pg_class AS relation +JOIN pg_namespace AS namespace ON namespace.oid = relation.relnamespace +WHERE namespace.nspname = 'workforce_validation'; +")" +if [[ "${relation_count}" != "0" ]]; then + echo "owner-schema bootstrap created application relations prematurely: ${relation_count}" >&2 + exit 1 +fi + +psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -qc "DROP ROLE workforce_validation_public_probe;" +trap - EXIT diff --git a/tests/validate_repository.py b/tests/validate_repository.py index d9d4c15a3..1a05c8efd 100644 --- a/tests/validate_repository.py +++ b/tests/validate_repository.py @@ -88,6 +88,7 @@ "tests/test_audit_outbox_hardening_postgres.sh", "tests/test_candidate_worker_conversion_postgres.sh", "tests/test_validity_study_case_postgres.sh", + "tests/test_workforce_validation_owner_schema_postgres.sh", "tests/test_criterion_observation_scope_postgres.sh", "tests/test_people_mutation_idempotency_postgres.sh", "tests/test_job_analysis_snapshot_postgres.sh", @@ -634,4 +635,4 @@ def main() -> None: if __name__ == "__main__": - main() + main() \ No newline at end of file