Skip to content

Metadata Review Queue — Curator Runbook

Workspace-scoped triage, assignment, audit, rollout, and rollback procedures for metadata review signals.

The Metadata review route is a workspace-scoped operational queue for deterministic metadata signals generated during enrichment, import, resync, and ingestion. It is not a public publication field and does not replace the Risk watchlist.

Code lives in apps/dashboard/src/features/metadata-review/, apps/dashboard/src/server/functions/metadata-review/, packages/feature-metadata-review/, and the AOA persistence boundary at packages/platform-ingestion/src/infra/db/metadata-review.ts.

First-release visibility and assignment policy

Section titled “First-release visibility and assignment policy”
  • Visibility: members of the active workspace with workspace_data:read, workspace admins, owners, and superadmins can read the queue. The server derives the workspace from the authenticated context; clients cannot select another workspace.
  • Actions: workspace admins/owners and superadmins can assign, start, resolve, defer, escalate, dismiss, and reopen metadata items. Regular members are read-only in the first release. Assignment accepts only an active member of the same workspace.
  • Risk signals: risk_retraction rows are read-only in this queue. Use the sibling Risk watchlist for retraction triage so its visibility-review and suppression rules remain authoritative.
  • Provenance: the ledger stores bounded evidence and the latest title snapshot. Raw provider payloads and publication fields remain authoritative; queue resolution never edits them by itself. Follow the linked publication/work workflow for corrections.

Reasons in the first release:

  • missing_title
  • missing_abstract
  • conflicting_publication_date
  • duplicate_candidate
  • enrichment_failed
  • risk_retraction (read-only projection of the Risk watchlist)

Active states are open, in_progress, deferred, and escalated. Human terminal states are resolved and dismissed; stale machine signals may become auto_resolved. Reappearing active signals reopen terminal enrichment items by their workspace/target/reason identity instead of creating duplicates.

  1. Open Metadata review from the workspace sidebar. The default view shows active items ranked by severity and age.
  2. Filter by reason, severity, assignee, age, or search text. Use Inspect to view bounded evidence and the current publication fields. A partial-read warning means the queue row is still valid; retry or follow the authoritative record link before deciding.
  3. Assign only to a same-workspace active member. Start an item before investigation when it is no longer immediately actionable.
  4. Resolve as fixed after correcting the authoritative publication/work record. Use False positive/dismiss only when the signal is not actionable, and record a concise note.
  5. Defer work that needs a later window and escalate items that need specialist attention.
  6. Reopen a terminal item when new evidence requires a fresh human decision. Every mutation writes a metadata.review.item.actioned activity event with actor, old/new state, target, reason, assignment, resolution, and note.

The expected-update timestamp is sent with each action. A conflict means another curator changed the row; refresh before retrying. No action silently crosses workspace scope.

The Review inbox route (/metadata-review-inbox, linked from the review queue) is an optional power-user surface over the same active queue. It composes the queue query and the review action contract above — it is not a second queue and never bypasses server-side authorization. The undo-versus-confirmation decision is recorded in docs/adr/0021-review-inbox-undo-before-confirmation.md: every v1 inbox action is reversible in-domain, so decisions ship with a bounded undo window instead of confirmation dialogs.

Shortcut map (also discoverable in-UI via the ? dialog):

KeysAction
j / , k / Move selection down / up (clamped at queue bounds)
Enter / oExpand or collapse the selected item’s preview
aAccept — resolve as fixed
xReject — resolve as false positive
d / eDefer / escalate
sStart review (not offered on in_progress items)
zUndo the most recent terminal decision (composes reopen)
rRefresh the queue
?Toggle the shortcut help dialog
EscapeClose help, or collapse the open preview

Behavior contract:

  • Focus model: focus follows selection (the selected row header is focused, aria-current marks it), and a live region announces the selected item with queue position plus every action outcome. After a successful decision the selection auto-advances to the next item; the undone item is re-selected on undo.
  • Undo (ADR-0021): terminal decisions (a, x) open an 8-second undo window served by the existing reopen action; both the decision and the undo write activity events. d, e, s, and assignment changes have no undo because they are non-terminal and directly reversible. Undo failures (for example a stale item) surface as errors and clear the offer.
  • Conflict behavior: every action sends expected_updated_at; a stale item returns 409, keeps its selection, and shows an alert instead of advancing. Risk-watchlist rows are never actionable here — the inbox points to the Risk watchlist.
  • Disabled states: read-only members get navigation without decision shortcuts; when a decision shortcut is unavailable (permissions, rollback mode, non-actionable item) the key is a no-op rather than an error. Shortcuts never fire while typing in a filter or form field and are never hijacked from modifier chords (Ctrl/Cmd/Alt).
  • Rollback lever: build with VITE_REVIEW_INBOX_NAV_ONLY=true to ship the inbox in navigation-only mode (movement, preview, and help still work; decision shortcuts and buttons are disabled with a notice). Reverting to normal actions is a rebuild, not a data change.
  • Worker, dashboard import, and dashboard resync flag persistence reports created, reopened, refreshed, auto-resolved, and failed counts in the metadata_review.flags_synced activity event. It also emits metadata.review.item.created, metadata.review.item.reopened, and metadata.review.item.auto_resolved lifecycle events with the affected DOI and count. Persistence is best effort and must not discard a successfully enriched publication.
  • Dashboard route metrics use metadata-review.list and include rows returned, total rows, and current-record read failures. Action audit events use the shared activity writer and the metadata.review.item.actioned event contract.
  • Review-write and current-field failures are captured to GlitchTip with workspace/target context. Inspect Worker logs and Cloudflare D1 query/error metrics for AOA (DB_0 in compatibility mode, DB_6 in target mode) and the routed transactional shard.

Useful rollout sample: record queue inflow by reason, active count, high-severity review lag, false-positive count, and time from open to terminal resolution for the pilot workspace.

  1. Apply 0050_metadata_review_items to the AOA write destinations only. During the default shadow/compatibility rollout this means both DB_0 (legacy source) and DB_6 (target); after the AOA target cutover DB_6 is the active destination. Do not add it to the shared transactional rebuild.
  2. Deploy ingestion/import and dashboard changes together, then verify an enriched publication creates at most one row per reason and that a second observation refreshes it.
  3. Expose the read surface to the pilot workspace. Keep regular members read-only and enable curator actions only after activity events and D1 writes are visible.
  4. Sample high-severity and false-positive outcomes before widening the pilot. Keep raw provider payloads available for comparison.

To roll back, hide the route or disable the dashboard action surface, then stop review flag writes at the ingestion/import call sites while retaining existing AOA rows and telemetry. Revert the application change first if necessary; drop the table only through the explicit rollback migrations/rollback/0050_metadata_review_items.sql after export/retention approval. Existing publication data and provider payloads are unaffected.