Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
33636b4
feat(response): persist accepted events so restart keeps the ledger
cursoragent Aug 16, 2026
a901fc6
fix(response): keep observed and received event times distinct
cursoragent Aug 16, 2026
86e5669
test(response): drop overflow time case that does not fail on this host
cursoragent Aug 16, 2026
4dd8e6d
feat(response): reload observed and received event clocks
cursoragent Aug 16, 2026
7a72ee1
test(response): prove sequence reuse by another event identity
cursoragent Aug 16, 2026
5c66242
fix(response): continue from reload and fail closed on gapped receipts
cursoragent Aug 16, 2026
4d1fbac
docs(response): name #221 as the persist/reload landing
cursoragent Aug 16, 2026
7de134b
fix(response): drop untestable timestamptz overflow arm
cursoragent Aug 16, 2026
18cdc05
test(recovery): preserve inbox claim deadline evidence
seonghobae Aug 16, 2026
27e793f
test(response): reject conflicting receipt history before return
seonghobae Aug 16, 2026
8074987
fix(response): validate receipt ledger before return
seonghobae Aug 16, 2026
ba6d8aa
test(response): reject write-time server sequence gaps
seonghobae Aug 16, 2026
3d8cbec
test(response): isolate unexpected unique-constraint fail-closed
seonghobae Aug 16, 2026
5a11276
style(response): backtick PostgreSQL in receipt-conflict rustdoc
seonghobae Aug 17, 2026
a2bf407
fix(response): reject write-time server sequence gaps before insert
seonghobae Aug 17, 2026
1bc6148
ci(supply-chain): restore protected-main SBOM contracts
seonghobae Aug 17, 2026
b4cd4ad
test(response): instantiate overflow millis and missing-table reload
seonghobae Aug 17, 2026
34e16cb
test(response): map missing-relation replay lookup to database error
seonghobae Aug 17, 2026
ab6be7b
Merge branch 'main' into cursor/bc-eeb726de-a42d-47c9-b890-9d57e5704a…
seonghobae Aug 17, 2026
bf2b02f
test(response): cover classify lookup through missing relation
seonghobae Aug 17, 2026
ec1e96d
test(response): rebind observed time independently of receipt time
seonghobae Aug 17, 2026
f755dae
test(response): instantiate next sequence from the library
seonghobae Aug 17, 2026
7fc05fb
test(response): instantiate persist and load in the library
seonghobae Aug 17, 2026
9db0aa3
Merge branch 'main' into cursor/bc-eeb726de-a42d-47c9-b890-9d57e5704a…
seonghobae Aug 17, 2026
21056aa
merge(main): reconcile outbox lease-expiry clock after #105
seonghobae Aug 17, 2026
73c7d51
test(response): instantiate persist and load isolation in the library
seonghobae Aug 17, 2026
f9da207
merge(main): reconcile data-rights access binding after #244
seonghobae Aug 17, 2026
922c86a
test(response): call require_read_committed from the library
seonghobae Aug 17, 2026
0cfdbd9
test(response): instantiate unique-violation classification in the li…
seonghobae Aug 17, 2026
181d3f3
test(response): instantiate persistence error Display and source
seonghobae Aug 17, 2026
f85aa2d
test(response): reject extension of corrupt sequence prefix
seonghobae Aug 17, 2026
6777f77
fix(response): reject corrupt stored sequence prefixes
seonghobae Aug 17, 2026
ffe3fd9
test(response): reject malformed migration state and Unicode aliases
seonghobae Aug 17, 2026
7d73e85
fix(response): fail closed on response-event schema drift
seonghobae Aug 17, 2026
25dc1ce
docs(evidence): bind PostgreSQL isolation source to controls
seonghobae Aug 17, 2026
2fb62fc
test(response): reject padded persistence aliases
seonghobae Aug 17, 2026
f59de6b
test(response): format migration contract
seonghobae Aug 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All notable product and architecture changes are recorded here. Releases use imm
## Unreleased

### Added
- PostgreSQL 18 persistence for accepted `response_event` rows so a mid-session restart can rebuild the same response ledger, continue from the reloaded prefix, and freeze the same scoring request. Observed time and platform receipt time stay distinct on write, reload, and recovery COPY. Exact replay is idempotent; conflicting client-event, sequence, evidence, time rebinding, or gapped receipt history fails closed under `READ COMMITTED`.
- Scoring-job cancel and lease-expiry fallback classification lock the current row until the caller transaction ends, so concurrent workers cannot rewrite terminal or unleased evidence.
- PostgreSQL operational-store readiness probe classifies the supported major version and write-readiness, and fails closed when a caller-declared required relation is missing.
- PostgreSQL scoring-job cancellation: queued, leased, or retry-scheduled work becomes cancelled without transferring a fence, exact replay is idempotent, and completed or quarantined evidence cannot be rewritten.
Expand Down
4 changes: 3 additions & 1 deletion docs/TRACEABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ An active PR, architecture document, conversation decision, or scheduler plan is
| Anonymous core assessment | PRD §3.1, §9.1 | TRD §5, §10; UML anonymous sequence | ADR-0002, ADR-0003, ADR-0005 | Session lifecycle primitives implemented, including creation bound to one published locale-specific release; anonymous credential/HTTP flow is Target |
| Pause/resume | PRD §3.1, §9.1 | TRD §5 | ADR-0005 | **Implemented** in `src/session.rs` with fail-closed transitions |
| Sequence-aware item delivery evidence | PRD §3.1, §9 | TRD §5–7 | ADR-0005, ADR-0010 | **Implemented** domain primitive in `src/item_delivery.rs`; persistence/API delivery orchestration is Target |
| Idempotent response events | PRD §9.2 | TRD §6 | ADR-0005, ADR-0010 | **Implemented** in `src/response.rs` with canonical SHA-256 payload-digest identity; persistence adapter is Target |
| Idempotent response events | PRD §9.2 | TRD §6 | ADR-0005, ADR-0010 | **Implemented** in `src/response.rs` with canonical SHA-256 payload-digest identity; mid-session `response_event` persist/reload with continue-from-restart and fail-closed gapped receipts is Active PR #221 in `src/postgres_response_event.rs` / `migrations/0020_response_event.sql` and is not protected-main truth until integrated |
| Immutable response snapshot before scoring | PRD §9.3 | TRD §5–8 | ADR-0005, ADR-0010 | **Implemented** domain semantics in `src/response.rs` |
| Version-pinned scoring | PRD §9.4, §10 | TRD §8 | ADR-0004, ADR-0010 | **Implemented** reusable product-side scoring dispatch contract in `src/scoring.rs` with canonical SHA-256 engine-artifact digest provenance plus `migrations/0011_scoring_request.sql` / `src/postgres_scoring_request.rs` request-identity persistence; live fast-mlsirm integration is Target |
| Bounded asynchronous scoring retry/quarantine with stale-worker fencing | PRD §9.4, §10 | TRD §8; ADR-0015 transaction boundary | ADR-0004, ADR-0010, ADR-0015 | **Implemented** product lifecycle plus PostgreSQL enqueue, claim, retry, completion, expiry recovery, and cancellation without transferring a fence; live fast-mlsirm execution remains Target |
Expand Down Expand Up @@ -134,6 +134,8 @@ Still-Target logical modules/adapters include remaining product aggregate persis

**Active PR** #60 exclusive outbox delivery-lease persistence is not protected-main truth until an unchanged reviewed/check-clean head is integrated. Pending outbox rows accept one fenced worker lease, recover expiry from the database clock without transferring the fence, reject a future caller timestamp that would steal a still-live lease, and reject stale or zero-window claims. Live side-effect execution remains outside this slice.

**Active PR** #221 response-event persist/reload stores each accepted answer under `response_event` so a mid-session restart can rebuild the same Korean IPIP Quick prefix, continue from the reloaded ledger, and freeze the same scoring request. Observed time and platform receipt time stay distinct on write, reload, and recovery COPY (`ResponseEventReceipt`). Exact replay is idempotent; client-identity, sequence, evidence, inverted/zero stored time, time rebinding, and gapped receipt history fail closed under `READ COMMITTED`. Neighbor sessions stay isolated by `session_ref`. Prefer this head over #182, #174, #53, snapshot-only #151, and stale overlapping draft #201. HTTP transport and live scoring remain outside this slice. This is not protected-main truth until an unchanged reviewed/check-clean head is integrated.

## 5. ADR traceability by concern

| Concern | Governing ADR(s) |
Expand Down
4 changes: 3 additions & 1 deletion docs/adr/0015-persistence-and-transaction-boundaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- Scope: Psychometrics Commons-owned durable state, local transactions, migration boundaries, outbox/inbox integration
- Supersedes: none
- Superseded by: none
- Current/as-built status: protected main contains in-memory/domain lifecycle primitives only; active PR #24 carries the first PostgreSQL integration-evidence migration/adapter but is not protected-main truth until merged
- Current/as-built status: protected main contains domain lifecycle primitives plus bounded PostgreSQL slices including completed `response_snapshot` persist; mid-session `response_event` persist/reload with distinct observed/received clocks is Active PR #221 work in `migrations/0020_response_event.sql` and is not protected-main truth until merged
- Target status: upstream PostgreSQL 18.x operational persistence with real-database concurrency/crash/recovery evidence and transactional outbox/inbox semantics
- Migration status: active PR #24 introduces only the bounded integration-evidence slice; the remaining product schema still must be established from the logical ERD and this ADR without synthetic provenance backfills

Expand Down Expand Up @@ -305,4 +305,6 @@ The physical database technology or decomposition may change if scale, residency

PostgreSQL Global Development Group. (2026). *PostgreSQL 18 documentation*.

PostgreSQL Global Development Group. (2026). *PostgreSQL 18 documentation: Transaction isolation*. https://www.postgresql.org/docs/18/transaction-iso.html

PostgreSQL Global Development Group. (2026). *PostgreSQL versioning policy*.
4 changes: 4 additions & 0 deletions docs/architecture/AS_BUILT_SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ The protected-main slice persists:

The slice does **not** persist publication-event history, bound scientific evidence records, HTTP publication transport, or session-creation integration. Those remain Target unless separately evidenced on protected main.

## Active PR response-event physical schema

PR #221 adds `migrations/0020_response_event.sql` and `src/postgres_response_event.rs` so each accepted answer is durable before snapshot freeze. The slice is **Active PR**, not protected-main truth. It stores opaque `response_event_ref` identity, session binding, client idempotency identity, item version, canonical SHA-256 payload digest, positive `server_sequence`, source-valid `observed_at`, and platform `received_at`. Exact replay is idempotent. Client-identity, sequence, evidence, and time rebinding fail closed. Reload reconstructs `ResponseLedger` and `ResponseEventReceipt` clocks in contiguous `server_sequence` order `1..=n` under `READ COMMITTED`. Gapped or reordered receipt history fails closed. A restart continues from the reloaded ledger and freezes the same scoring prefix. Inverted or zero stored times fail closed even if a check constraint was dropped. Recovery COPY preserves distinct observed/received clocks. HTTP response transport remains outside this slice.

## Logical-to-physical mapping rule

A logical entity is classified as physical only when all of the following exist on the named protected-main baseline:
Expand Down
2 changes: 2 additions & 0 deletions docs/architecture/ERD.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ The target ERD deliberately includes several logical entities that are not yet p
- `instrument_release` is the locale-specific publication identity already owned by `src/instrument.rs`. Physical `migrations/0006_instrument_release.sql` persists that one-row aggregate (immutable manifest columns plus `publication_state`); HTTP publication transport remains Target.
- `data_rights_request` and `data_rights_propagation_state` are the first durable export/deletion slice. Physical `migrations/0003_data_rights_propagation.sql` stores requested-state identity plus one local outbox event per dependent system; verification, processing, completion, and dependent-system execution remain Target.
- `item_delivery_event` reflects the already-merged `src/item_delivery.rs` domain primitive; durable persistence/API orchestration is still Target.
- `response_event` is the accepted mid-session ledger. Physical `migrations/0020_response_event.sql` and `src/postgres_response_event.rs` persist/reload that prefix plus distinct observed/received clocks on Active PR #221; HTTP response transport remains Target.
- `consent_ledger` and `consent_event` persist the already-merged `src/consent.rs` append-only ledger. Physical persistence is carried by Active PR #49 (`migrations/0005_consent_lifecycle.sql`); HTTP consent transport and derived snapshot tables remain Target.
- `participant_identity_link` is the persistence target accepted by ADR-0020. The current `src/participant.rs` `keyverse_subject_ref` field is an application-domain first-link projection, not the future mutable persistence source of truth.
- `longitudinal_enrollment`, `longitudinal_observation_record`, and `temporal_analysis_submission` make the ADR-0008 Commons-owned Gyeot/TEPP orchestration boundary explicit. No TEPP analytical kernel is duplicated here.
Expand Down Expand Up @@ -458,6 +459,7 @@ Once semantically published/frozen, the following are append-only or superseded
- `instrument_release` manifest columns after first persist (only `publication_state` may advance);
- `item_version` after publication;
- `item_delivery_event`;
- accepted `response_event` rows;
- `response_snapshot` and `response_snapshot_entry`;
- `result_snapshot`;
- `consent_snapshot`;
Expand Down
1 change: 1 addition & 0 deletions docs/architecture/UML.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ sequenceDiagram
participant F as fast-mlsirm scoring path

P->>A: complete assessment
Note over DB: Active PR persist/reload keeps accepted response_event rows and distinct observed/received clocks so a mid-session restart can rebuild the same prefix before this freeze
A->>DB: commit Completed + immutable response snapshot + outbox
A-->>P: completion durable; scoring pending

Expand Down
21 changes: 17 additions & 4 deletions docs/doctoring/standards-and-evidence.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Standards and Evidence Baseline

- Status: Living doctoring record
- Last reviewed: 2026-08-11
- Last reviewed: 2026-08-18
- Scope: Psychometrics Commons product, hosted runtime, reference clients, optional AI, identity integration, and assessment governance

This record identifies authoritative standards and primary guidance that materially constrain product design. It is not a certification claim. Each implementation PR that relies on one of these sources must translate the source into a concrete requirement, test, control, or ADR rather than citing it decoratively.
Expand Down Expand Up @@ -74,6 +74,17 @@ Product consequences:
- higher-impact AI changes require an impact/risk assessment proportional to intended use and affected participants;
- model/prompt/provider drift is observable and does not silently alter historical results.

## PostgreSQL transaction isolation and immutable replay

PostgreSQL 18 `READ COMMITTED` gives each command a fresh snapshot. Psychometrics Commons persistence adapters that use a conflict-tolerant insert followed by an exact-replay verification read therefore require `READ COMMITTED` when that second command must observe a concurrently committed unique-key winner. They fail closed on stronger transaction isolation instead of assuming that a later command receives a newer snapshot. ADR 0015 records the repository transaction boundary, and PostgreSQL integration tests exercise both exact replay and unsupported-isolation rejection.

Product consequences:

- persistence APIs declare their required isolation level as part of the adapter contract;
- an idempotent replay verifies all immutable identity/evidence fields rather than treating a unique-key conflict as success;
- unsupported isolation fails before durable mutation where the adapter depends on command-level snapshot refresh;
- concurrency behavior is verified against the repository-supported PostgreSQL major version rather than inferred from an in-memory mock.

## Temporal and provenance evidence

Longitudinal observations distinguish validity time from source-recorded time,
Expand Down Expand Up @@ -107,6 +118,8 @@ Product consequences:

American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. https://www.testingstandards.net/

International Organization for Standardization. (2019). *ISO 8601-1:2019 Date and time—Representations for information interchange—Part 1: Basic rules* (with Amendment 1:2022). https://www.iso.org/standard/70907.html

International Organization for Standardization. (2022). *ISO/IEC 27001:2022 Information security, cybersecurity and privacy protection—Information security management systems—Requirements* (3rd ed.). https://www.iso.org/standard/27001

International Organization for Standardization. (2023a). *ISO/IEC 23894:2023 Information technology—Artificial intelligence—Guidance on risk management*. https://www.iso.org/standard/77304.html
Expand All @@ -117,10 +130,10 @@ International Organization for Standardization. (2024). *ISO/IEC 27001:2022/Amd

International Organization for Standardization. (2025). *ISO/IEC 42005:2025 Information technology—Artificial intelligence (AI)—AI system impact assessment*. https://www.iso.org/standard/42005

International Organization for Standardization. (2019). *ISO 8601-1:2019 Date and time—Representations for information interchange—Part 1: Basic rules* (with Amendment 1:2022). https://www.iso.org/standard/70907.html
The PostgreSQL Global Development Group. (2026). *PostgreSQL 18 documentation: Transaction isolation*. https://www.postgresql.org/docs/18/transaction-iso.html

Temoshok, D., Proud-Madruga, D., Choong, Y.-Y., Galluzzo, R., Gupta, S., LaSalle, C., Lefkovitz, N., & Regenscheid, A. (2025). *Digital identity guidelines* (NIST Special Publication 800-63-4). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-63-4

World Wide Web Consortium. (2024). *Web Content Accessibility Guidelines (WCAG) 2.2* (W3C Recommendation, 12 December 2024). https://www.w3.org/TR/WCAG22/

World Wide Web Consortium. (2013). *PROV-DM: The PROV data model* (W3C Recommendation, 30 April 2013). https://www.w3.org/TR/prov-dm/

World Wide Web Consortium. (2024). *Web Content Accessibility Guidelines (WCAG) 2.2* (W3C Recommendation, 12 December 2024). https://www.w3.org/TR/WCAG22/
Loading
Loading