Human review: reviewer roles, approvals, and rejections
Source:vignettes/articles/09-human-review-decisions.Rmd
09-human-review-decisions.RmdHuman review binds decisions to one revision’s retained script,
image, and analytical output. A revision reaches Reviewed
only after approvals from distinct active non-creator actors covering
both statistical_programmer and
biostatistician roles. Successful execution alone produces
Verified; it does not supply either human approval.
This page describes implementation inspected at base commit
5e2fa6a. It documents the local proof of concept, not
authenticated production review or formal validation evidence.
Using the review panel
The Export step contains the Two-Person Review panel. The reviewer selects a local identity and a review role, enters an optional approval comment or required rejection rationale, and chooses Approve or Reject. The panel then shows recorded actor IDs, roles, decisions, and timestamps. Selecting the creator identity remains possible in the interface, but the repository refuses that actor’s decision on their own revision.
mod_review_server()
reloads the revision before submitting a decision. It supplies the
current integer version and an idempotency key built from the decision,
revision ID, actor ID, and version. On success it refreshes application
state; on failure it displays the error message. The role selector does
not grant that role: the repository checks the claim against the actor’s
registered roles.
Decision checks and outcomes
new_review_service()
resolves an actor through the identity provider. Its
approve() and reject() methods delegate to
record_review_decision() in sqlite_evidence_repository().
When callers omit hashes, the service reads the revision’s
code_hash, image_hash, and
analytical_hash and submits those values.
The repository applies these checks within its decision transaction:
- The revision must be
Verified, and the submitted version must equal its current version.Reviewedrevisions are immutable;Draft,Experimental/Draft, andRejectedrevisions cannot accept new review decisions. - All three submitted artifact hashes must match the revision. A changed script, image, or analytical result cannot reuse an approval for different bytes or content identity.
- The actor must be active, must differ from the revision creator, and must hold an eligible reviewer role. An actor holding both roles must explicitly select one authorized role.
- An actor who already recorded a decision on this revision cannot record another under a new command. One person with both roles cannot provide both required approvals.
- Rejection requires a rationale satisfying the implementation’s nonempty-string check. Approval comments are optional.
After the first approval, the status remains Verified,
and the version increments. Another approval may arrive in either role
order. Once recorded approvals include at least two distinct actors and
both required roles, the repository sets Reviewed. Two
approvals covering only one role leave the revision
Verified until another eligible actor supplies the missing
role.
A valid rejection immediately sets Rejected, including
when an earlier approval exists. It prevents further decisions on that
revision. A competing request using the previous version fails the
stale-token check. Correction creates a successor rather than reopening
the rejected revision; see correction
revisions.
Records and repeat requests
Each decision retains its decision and revision IDs, actor ID, claimed role, decision, authorization snapshot, comment or rationale, three artifact hashes, UTC timestamp, idempotency key, and record hash. The authorization snapshot records the actor’s ID, roles, and active flag at decision time.
The repository appends a review_approved or
review_rejected lifecycle event containing the resulting
status, actor, role, decision ID, and decision hash.
list_review_decisions() returns the stored decisions
ordered by timestamp and decision ID. Integrity verification compares
their artifact hashes and record hashes with the revisions and lifecycle
history.
Repeating an identical registered command with the same idempotency key returns without adding another decision. Reusing a key for changed command content is rejected. This repeat handling is separate from the distinct-actor rule applied to new commands.
Enforcement limits
local_identity_provider()
looks up actors in a supplied catalog. app_server()
constructs creator, programmer, and biostatistician actors locally, and
the interface lets the user select them. These mechanisms demonstrate
role and identity rules but do not authenticate the person operating the
session. Error messages referring to an “authenticated” actor do not
establish authentication.
The displayed preview is rebuilt in the application process rather than loaded from the accepted worker PNG. Decisions bind retained revision hashes, but the current interface does not establish that the image visually inspected was the accepted image. Review does not automatically invoke retained-input replay. See execution evidence for the distinction between automated checks and human judgment.
Rejection is implemented directly by the repository’s decision
transaction. The generic transition_revision_status()
helper lists Verified to Reviewed but has no
rejection transition; it is not the complete review lifecycle. Export
requires Reviewed status plus the remaining bundle,
authorization, destination, and integrity checks described in controlled export.
Inspected coverage
test-review-service.R
covers creator exclusion, active actors, dual-role claims, distinct
identities, exact hashes, rationale retention, stale versions, either
approval order, and competing decisions. test-mod-review.R
exercises the panel’s two-approval state update. These files were
inspected, not executed for this documentation change. Browser behavior,
authentication, statistical adequacy of reviewer decisions, and
regulated qualification remain unverified here.
