Skip to content
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
- After restart, `list_startable_instrument_releases` returns stored Published forms ordered by instrument, locale, then release reference so a worker can offer only currently startable catalogs. Draft, Review, Suspended, and Retired rows are omitted. A corrupt Published row fails closed as a stored-snapshot reconstruction error instead of appearing startable. `load_instrument_release` still reconstructs any stored publication state by release reference. Exact persist replay of a listed snapshot stays Duplicate. Publication event history and bound evidence are not in this adapter. Session start from one stored Published locale-matched release remains #180. HTTP catalog transport remains #165.
- 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: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Psychometrics Commons owns product APIs, instrument publication, participant/ses

It does **not** duplicate psychometric numerical kernels, identity credentials, temporal model kernels, public research catalog internals, or generic LLM orchestration. `g7` is an optional replaceable reference client rather than a platform dependency.

## Instrument catalog after restart

After a worker restart, open a `READ COMMITTED` transaction and call `list_startable_instrument_releases`. Copy a returned `release_ref` and exact `locale` into session start. Draft, Review, Suspended, and Retired forms are omitted. If listing fails, repair the corrupt stored snapshot before offering any form. Do not start sessions from a partial untrusted catalog.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads as shipped product behavior. Traceability still marks the catalog as Active PR work. Successor #213 marks the caveat and sends the copied release_ref + exact BCP 47 locale into #180 load_published_instrument_release, not a generic session-start path.


## Documentation

### Product, technical, and governance baseline
Expand Down
10 changes: 6 additions & 4 deletions docs/TRACEABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ An active PR, architecture document, conversation decision, or scheduler plan is
| Immutable result provenance | PRD §3.1, §9.4 | TRD §9 | ADR-0004, ADR-0010 | **Implemented** in `src/result.rs`; result-serving transport is Target |
| Deterministic narrative fallback | PRD §3.2, §9.5 | TRD §17; Architecture narrative view | ADR-0009, ADR-0010, ADR-0018 | Target |
| Continuous scores remain source of truth; Personality Style is presentation | PRD §3.2 | Measurement Governance; AI Governance | ADR-0018 | Target product narrative mapping; numeric source remains External fast-mlsirm contract |
| Immutable instrument release/version lifecycle | PRD §6, §9 | TRD §7; UML publication state | ADR-0005, ADR-0010 | **Implemented** in `src/instrument.rs` plus `migrations/0006_instrument_release.sql` and `src/postgres_instrument_release.rs`: immutable release manifest, exact version/digest/locale/item set, fail-closed Draft/Review/Published/Suspended/Retired lifecycle, idempotent publication events, and new-session eligibility |
| Immutable instrument release/version lifecycle | PRD §6, §9 | TRD §7; UML publication state | ADR-0005, ADR-0010 | **Implemented** persist and domain lifecycle in `src/instrument.rs` plus `migrations/0006_instrument_release.sql` and `src/postgres_instrument_release.rs`. Reload-after-restart and startable-catalog listing of stored Published forms are Active PR work; HTTP publication/catalog transport remains Target |
| Instrument publication requires intended-use scientific/right/locale evidence | PRD §6, §9, §10 | Measurement Governance; publication evidence gate | ADR-0004, ADR-0013, ADR-0019 | **Implemented** policy gate and immutable evidence provenance in `src/instrument.rs`; each real instrument still requires its own rights/locale/scientific evidence artifacts before publication |
| Optional Keyverse account linking | PRD §3.1, §9.7 | TRD §10; UML identity-link lifecycle | ADR-0003, ADR-0020 | **Partially implemented**: issuer-scoped first-link fail-closed domain primitive in `src/participant.rs`; append-only unlink/relink/recovery history, persistence, audit, and transport remain Target |
| Cross-cutting tenant/task authorization | PRD §7, §9 | TRD §11; Security/Data | ADR-0001, ADR-0003 | **Implemented** fail-closed domain gate in `src/authorization.rs` binds consent operations to participant-owned `ConsentLedger` / `ManageOwnConsent`; persistence/policy-adapter/public-transport integration remains Target |
Expand Down Expand Up @@ -62,8 +62,8 @@ An active PR, architecture document, conversation decision, or scheduler plan is
| Scientific failure is typed, no invented score | TRD §8; Measurement Governance | scoring contract tests | cross-process failure injection |
| Historical result does not mutate | TRD §9 | `src/result.rs` snapshot semantics | persistence and API supersession tests |
| Narrative cannot mutate score / deterministic fallback exists | AI Governance; ADR-0018 | architecture policy | mapping implementation + canonical style-assignment key + fallback/no-score-mutation tests |
| Instrument release bytes/version/item order are immutable | TRD §7 | `src/instrument.rs` + publication contract tests; `src/postgres_instrument_release.rs` persists immutable manifest columns | API publication integration |
| Only Published release accepts new sessions | TRD §7 | `PublicationState::accepts_new_sessions` in `src/instrument.rs`; `AssessmentSession` creation copies exact published release/version/locale provenance and fails closed on unpublished eligibility or locale mismatch | session-creation persistence/API integration test |
| Instrument release bytes/version/item order are immutable | TRD §7 | `src/instrument.rs` + publication contract tests; `src/postgres_instrument_release.rs` persists immutable manifest columns; reload-after-restart is Active PR work | API publication integration |
| Only Published release accepts new sessions | TRD §7 | `PublicationState::accepts_new_sessions` in `src/instrument.rs`; `AssessmentSession` creation copies exact published release/version/locale provenance and fails closed on unpublished eligibility or locale mismatch; persisted-state reload and startable-catalog listing are Active PR work | session-creation persistence/API integration test |
| Publication event replay is idempotent/conflicting reuse fails closed | TRD §7 | `src/instrument.rs` | durable DB uniqueness/concurrency test |
| Published instrument requires exact-version scientific evidence | Measurement Governance; ADR-0019 | `src/instrument.rs` binds approved evidence status, provenance/scope, mandatory evidence references, validity window, and immutable release identity before publication/reactivation | persistence/API publication integration and real instrument-specific evidence artifacts |
| Optional account linking does not rewrite historical participant/result identity | ADR-0003, ADR-0020 | `src/participant.rs` issuer-scoped first-link primitive preserves stable participant ID | append-only identity-link persistence + unlink/relink/recovery audit tests |
Expand Down Expand Up @@ -132,7 +132,9 @@ Still-Target logical modules/adapters include remaining product aggregate persis

### Active implementation work that is not protected-main truth

**Active PR** #76 data-rights processing-start persistence is not protected-main truth until an unchanged reviewed/check-clean head is integrated. Identity-verified requests persist an immutable operation identity and processing-start time under `FOR UPDATE` so later lifecycle composition cannot race the classified row. Dependent-system execution remains outside this slice.
**Active PR** #193 instrument-release startable-catalog listing is not protected-main truth until an unchanged reviewed/check-clean head is integrated. Prefer this head over #179 and #171 for catalog-from-store. After restart, `list_startable_instrument_releases` returns stored Published forms ordered by `instrument_ref`, `locale`, then `release_ref`. Draft, Review, Suspended, and Retired rows are omitted. A corrupt Published row fails closed as a stored-snapshot reconstruction error so the catalog cannot treat damaged evidence as startable. `load_instrument_release` still reconstructs any stored publication state by `release_ref`. Exact persist replay of a listed snapshot stays Duplicate. Publication event history and bound evidence are not stored by this adapter. This is complementary to #180 `load_published_instrument_release`, which is the session-start landing vehicle, and to #165, which is in-process HTTP catalog only. Do not merge this slice in parallel with #180; rebase after #180. Do not fold HTTP or session start into this adapter list. Do not merge #164 or #179 in parallel. `src/postgres_instrument_release.rs` and `migrations/0006_instrument_release.sql` are the adapter/migration for this slice.

Data-rights processing-start persistence from #76 is on this branch ancestry and is no longer Active PR work. Dependent-system execution remains Target.

## 5. ADR traceability by concern

Expand Down
2 changes: 2 additions & 0 deletions docs/architecture/AS_BUILT_SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ The protected-main slice persists:
- reachable publication-state advance without rewriting immutable manifest columns;
- fail-closed digest/identity rebinding and unreachable lifecycle rewind.

Reload of the stored locale, digest, item set, and publication state after process restart is Active PR work and is not protected-main truth until an unchanged reviewed/check-clean head is integrated. After that lands, call `list_startable_instrument_releases` to offer only currently Published forms, then call `load_instrument_release` with the chosen release reference before starting a new session.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_instrument_release still rebuilds Draft/Review/Suspended/Retired. Session start is #180 load_published_instrument_release. #213 updates this next-action sentence so a catalog row cannot be treated as an unpublished-state loader.


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.

## Logical-to-physical mapping rule
Expand Down
4 changes: 3 additions & 1 deletion 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-16
- 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 All @@ -13,6 +13,8 @@ The product's core scientific governance follows the *Standards for Educational
Product consequences:

- an instrument release states intended score interpretations and prohibited/unsupported uses;
- a published form remains reconstructable after process restart from its stored locale, digest, item set, and publication state so administration uses the same versioned instrument the participant was assigned;
- after restart, only currently Published stored forms are offered as startable catalog entries so withdrawn, draft, or damaged work cannot be administered as if it were the current form;
- scoring and norms are versioned and reproducible;
- precision/uncertainty is not hidden behind a point estimate;
- translated forms and group comparisons require evidence appropriate to the intended comparison;
Expand Down
31 changes: 31 additions & 0 deletions src/instrument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,37 @@ impl InstrumentRelease {
})
}

/// Rebuild one persisted instrument-release snapshot after process restart.
///
/// Call this with the stored manifest, publication state, and creation time
/// before starting a new session. If the reconstructed state is
/// [`PublicationState::Published`], start sessions on that exact locale,
/// digest, and item set. Event history and bound publication evidence are
/// not part of the persist snapshot, so reactivation still requires the
/// caller to bind approved evidence again.
///
/// # Errors
///
/// Returns [`InstrumentReleaseError::InvalidTimestamp`] when the stored
/// creation time is zero.
pub fn from_persisted_snapshot(
manifest: InstrumentReleaseManifest,
state: PublicationState,
created_at_unix_ms: u64,
) -> Result<Self, InstrumentReleaseError> {
if created_at_unix_ms == 0 {
return Err(InstrumentReleaseError::InvalidTimestamp);
}
Ok(Self {
manifest,
publication_evidence: None,
state,
created_at_unix_ms,
latest_event_at_unix_ms: created_at_unix_ms,
events: Vec::new(),
})
}

/// Return the immutable release manifest.
#[must_use]
pub const fn manifest(&self) -> &InstrumentReleaseManifest {
Expand Down
Loading
Loading