trevor — Domain Model¶
Entity relationship overview¶
Project (from CRD)
│
├─── ProjectMembership ──── User
│ (role: researcher / output_checker / senior_checker)
│
└─── AirlockRequest
│ direction: ingress | egress
│ status: DRAFT → ... → RELEASED | REJECTED
│
├─── OutputObject (version 1)
│ │ statbarn, checksum, storage_key
│ │ state: PENDING | APPROVED | SUPERSEDED | ...
│ │
│ └─── OutputObjectMetadata ← shared across versions
│ (title, description, justification,
│ suppression_notes, checker_feedback[])
│
├─── OutputObject (version 2, replaces version 1)
│ │ same metadata record, updated
│ └─── (checksum, storage_key for new file)
│
├─── Review (agent)
│ reviewer: agent:trevor-agent
│ decision: changes_requested
│ findings[]: per-object structured feedback
│
├─── Review (human checker)
│ reviewer: user_id
│ decision: approved
│ findings[]: per-object structured feedback
│
└─── AuditEvent[] (append-only log of all transitions)
Core entities¶
User¶
Synced from CR8TOR User CRDs on reconcile. keycloak_sub is populated on first successful Keycloak login. trevor holds this local shadow record for audit FK integrity and to support checker assignment before a user's first login.
| Field | Type | Notes |
|---|---|---|
id |
UUID | trevor-internal (not the CR8TOR project UUID) |
keycloak_sub |
str | null | Keycloak subject claim — set on first login |
username |
str | From spec.username in User CRD — matches Keycloak preferred_username |
email |
str | From spec.email |
given_name |
str | From spec.given_name |
family_name |
str | From spec.family_name |
affiliation |
str | From spec.affiliation |
crd_name |
str | metadata.name from User CRD |
active |
bool | From spec.enabled; false blocks login |
synced_at |
datetime | Last CRD sync |
created_at |
datetime |
Project¶
Read from CR8TOR research.karectl.io/v1alpha1/Project CRDs. The canonical project UUID comes from the cr8tor.io/project-id label, ensuring consistency with the rest of the karectl platform.
| Field | Type | Notes |
|---|---|---|
id |
UUID | From cr8tor.io/project-id label on Project CRD |
crd_name |
str | metadata.name from Project CRD (e.g. lancs-tre-proj-1) |
display_name |
str | From spec.description; falls back to metadata.name |
status |
enum | active (CRD present) / archived (CRD deleted from cluster) |
synced_at |
datetime | Last CRD reconcile |
ProjectMembership¶
Defines a user's role within a project. A user may have different roles on different projects.
| Field | Type | Notes |
|---|---|---|
user_id |
UUID FK | |
project_id |
UUID FK | |
role |
enum | researcher / output_checker / senior_checker |
assigned_by |
UUID FK | tre_admin who assigned |
assigned_at |
datetime |
Constraint: A user with researcher role on project P MUST NOT also hold output_checker or senior_checker on project P.
AirlockRequest¶
The primary workflow entity.
| Field | Type | Notes |
|---|---|---|
id |
UUID | |
project_id |
UUID FK | |
direction |
enum | egress / ingress |
status |
enum | See lifecycle in Glossary |
title |
str | Researcher-supplied |
description |
str | |
submitted_by |
UUID FK | User |
submitted_at |
datetime | |
updated_at |
datetime | |
closed_at |
datetime | null until terminal state |
OutputObject¶
An immutable file submitted as part of a request.
| Field | Type | Notes |
|---|---|---|
id |
UUID | |
request_id |
UUID FK | |
version |
int | 1-indexed within a lineage chain |
replaces_id |
UUID FK | null for v1; points to previous version |
logical_object_id |
UUID | Shared across all versions in a lineage |
filename |
str | Original filename |
output_type |
enum | See Output Type in Glossary |
statbarn |
str | Statbarn code (researcher-assigned, checker-verifiable) |
storage_key |
str | Key in quarantine S3 bucket |
checksum_sha256 |
str | Computed at upload |
size_bytes |
int | |
state |
enum | PENDING / APPROVED / REJECTED / CHANGES_REQUESTED / SUPERSEDED |
uploaded_at |
datetime | |
uploaded_by |
UUID FK |
OutputObjectMetadata¶
One record per logical_object_id. Accumulates annotations across versions.
| Field | Type | Notes |
|---|---|---|
logical_object_id |
UUID PK | FK to the lineage |
title |
str | |
description |
str | |
researcher_justification |
str | Why this output is necessary |
suppression_notes |
str | What SDC was applied |
checker_feedback |
JSON | Array of {reviewer_id, version, feedback, timestamp} |
tags |
JSON | Arbitrary key-value pairs |
updated_at |
datetime |
Review¶
A recorded decision from a checker (human or agent) on a whole request.
| Field | Type | Notes |
|---|---|---|
id |
UUID | |
request_id |
UUID FK | |
reviewer_id |
UUID FK | null for agent |
reviewer_type |
enum | human / agent |
decision |
enum | approved / rejected / changes_requested |
summary |
str | Overall comment |
findings |
JSON | Array of {object_id, decision, feedback} |
created_at |
datetime |
AuditEvent¶
Append-only log. Never updated or deleted.
| Field | Type | Notes |
|---|---|---|
id |
UUID | |
request_id |
UUID FK | null for system events |
actor_id |
str | User UUID or agent:trevor-agent or system |
event_type |
str | Namespaced: request.submitted, object.uploaded, review.created, request.released, etc. |
payload |
JSON | Event-specific detail |
timestamp |
datetime | UTC, server-set |
ReleaseRecord¶
Created when a request reaches RELEASED state.
| Field | Type | Notes |
|---|---|---|
id |
UUID | |
request_id |
UUID FK | |
crate_storage_key |
str | Key in release S3 bucket |
crate_checksum_sha256 |
str | |
presigned_url |
str | Current signed URL |
url_expires_at |
datetime | |
delivered_to |
JSON | List of email addresses notified |
created_at |
datetime |
State machine: AirlockRequest¶
┌─────────┐ submit() ┌───────────┐ agent completes ┌──────────────┐
│ DRAFT │ ─────────────► │ SUBMITTED │ ─────────────────► │ AGENT_REVIEW │
└─────────┘ └───────────┘ └──────┬───────┘
│ agent report ready
┌──────▼───────┐
┌───────────────────────────── │ HUMAN_REVIEW │
│ changes_requested └──────┬───────┘
▼ │ approved / rejected
┌────────────────────┐ ┌─────────▼──────────┐
│ CHANGES_REQUESTED │ │ APPROVED / REJECTED │
└──────────┬─────────┘ └─────────┬──────────┘
│ researcher resubmits │ (if approved)
└──────────────────────────────► ┌──────▼───────┐
│ RELEASING │
└──────┬───────┘
│ crate built + URL sent
┌──────▼───────┐
│ RELEASED │
└──────────────┘
Lineage chain: OutputObject versions¶
logical_object_id: 7f3a...
│
┌───────────┴────────────┐
│ │
OutputObject v1 OutputObject v2
id: aaa id: bbb
replaces: null replaces: aaa
state: SUPERSEDED state: PENDING
storage_key: /... storage_key: /...
checksum: abc123 checksum: def456
│
OutputObjectMetadata (logical_object_id: 7f3a...)
— title set by researcher at v1
— checker_feedback from v1 review carried forward
— suppression_notes updated by researcher at v2
Notification model¶
Notifications are triggered by state transitions and delivered via a pluggable backend. Supported backends: SMTP (default), webhook, in-app (database-backed notification table). Multiple backends can be active simultaneously.
Events that trigger notifications:
| Event | Recipient(s) |
|---|---|
request.submitted |
Assigned checkers |
review.created (agent) |
Assigned checkers (agent report ready) |
request.changes_requested |
Submitting researcher |
request.approved |
Submitting researcher, project lead |
request.rejected |
Submitting researcher |
request.released |
Submitting researcher, designated download recipients |
presigned_url.expiring_soon |
Submitting researcher |