# Journey: Editorial review and approval

This journey separates the editorial lifecycle the product requires from the
review seams that are actually shipped. The target path is author submit →
automated policy review → human decision → publication gate → customer release
with a reconstructable audit trail. Today, `/operator/studio` presents a
seven-row fixture queue and one detailed Tara artifact; its decision row can
create a real, audited editorial release stream for **approve** or **request
changes**. Author submission, live queue ingestion, a reject control on this
surface, final publication, and provenance-bundle emission are not connected.

The button label “Approve · publish 06:00” is therefore ahead of the runtime:
the click creates a `hotfix` release stream and moves it to `in_progress`; it
does not publish customer content at 06:00. This page uses “approve” only for
that recorded stream transition and reserves “publish” for the unwired final
release leg.

## Personas

- **Studio author** — prepares a customer artifact; the author-side submit
  endpoint remains unconfirmed.
- **Editorial operator** — has `admin:*`, `admin:studio`, or
  `admin:workspace:editorial` and can operate the release-stream seam.
- **Domain or theology reviewer** — represented in the fixture notes; no
  assignment engine is wired to the queue.
- **Audio reviewer** — represented by the seeded time-coded audio note.
- **Lilith Auto reviewer** — represented by a seeded confidence note; the
  current page does not run an automated review.

## Pre-conditions

- The operator is authenticated and authorized for `/operator/studio`.
- `getStudioReview()` currently resolves `getStudioReviewFixture()`: seven rows
  (`TR-047`, `TR-048`, `VR-112`, `NS-018`, `AR-031`, `NX-022`, `TR-049`), four
  review notes, and three citation rows.
- The actionable artifact is fixed to `TR-047`. Queue rows display status and
  selected styling, but do not currently change the central artifact when
  clicked.
- The release-stream BFF and audit store are reachable. Approve/request-changes
  calls use `POST /v1/admin/editorial/release-streams`, followed by `PATCH` on
  the returned stream.
- LocalStorage is available if the browser is expected to restore the visible
  decision receipt after reload. The BFF stream is real; this particular UI does
  not fetch the stream back on mount.

## Steps

### 1. Author submits a draft — target contract

- [ ] The author chooses “Submit for review” from a Studio artifact.
- [ ] The confirmation exposes artifact identity, target surface, declared tone
      band, source set, and review expectations.
- [ ] An author-scoped BFF endpoint records the submission and emits an
      `editorial.submit_for_review` audit event.
- [ ] The resulting artifact enters the operator queue in `in review` state.

No author-side submit route currently feeds `/operator/studio`; do not use the
operator release-stream endpoint as evidence for this step.

### 2. Automated pre-review — target contract

- [ ] Lilith and Sophia evaluate the submitted artifact, not a fixture.
- [ ] The review records confidence, unsupported-claim count, contradiction
      count, policy hits, and the exact policy/source-set versions.
- [ ] A weak result blocks approval and opens the appropriate operator work.
- [ ] The automated note is audit-linked to the submitted artifact.

The visible `who="Lilith", role="Auto"` note is static fixture copy. It proves
the intended presentation, not that a classifier ran.

### 3. Operator opens the current review surface

- [x] Navigate to `/operator/studio`.
- [x] The operator bar identifies Lilith Studio / Editorial and the Tara review
      breadcrumb.
- [x] The left column exposes seven fixture rows with artifact code, status,
      title, byline, and one selected marker.
- [x] The central pane exposes the fixed `TR-047` title, byline, Audio /
      Transcript / Confidence chips, transcript, and the decision row.
- [x] The right pane exposes four seeded notes and three seeded citations.
- [ ] Selecting another queue row updates the artifact, notes, citations, and
      decision target. The rows are currently non-interactive `<div>` elements.

### 4. Reviewer inspects the evidence

- [x] The current artifact shows a deterministic 120-bar SVG waveform with
      static time labels.
- [x] Transcript excerpts and time-coded fixture notes are readable.
- [x] Citation rows expose `verified` and `pending` status text, so status is
      not communicated by color alone.
- [ ] Waveform play, pause, and scrub controls operate. The waveform is
      presentation-only today.
- [ ] A pending citation opens Sophia source detail.
- [ ] A reviewer can add or edit a time-coded note.

### 5. Record an approve decision

- [x] Optionally select one or more local annotation toggles: revoice, pacing,
      or re-cite source.
- [x] Choose “Approve · publish 06:00”.
- [x] `StudioReviewDecisionRow` creates a `hotfix` release stream for `TR-047`
      with a generated next-06:00 target and then patches the stream from
      `pending` to `in_progress`.
- [x] The UI renders the returned stream ID and status and stores the visible
      receipt locally for reload continuity.
- [x] The BFF records `admin.editorial_release_stream.hotfix.created` and
      `admin.editorial_release_stream.hotfix.in_progress` audit events.
- [ ] Require a reviewer rationale for approval. The current approve path
      supplies generated route copy instead of asking the reviewer.
- [ ] Re-run source, claim, persona-policy, and provenance gates before a
      customer release.

“Reopen review” currently resets the browser state only. It does not cancel or
reverse the already-created BFF release stream.

### 6. Record a request-changes decision

- [x] Choose “Request changes” to expose the rationale field.
- [x] Enter at least 20 trimmed characters; shorter copy leaves “Send to author”
      inactive.
- [x] Submit. The component creates a `hotfix` stream and patches it to
      `blocked` with blocker `other` and the supplied rationale.
- [x] The returned stream receipt is visible and the local UI restores it after
      reload.
- [ ] Notify the author and attach the decision to an author-editable draft. The
      BFF stream records the blocker, but no author delivery is wired from this
      component.

### 7. Reject — adjacent seam, not this queue

- [x] `/operator/admin` has a separate `AdminInboxDecisionPanel` with verdicts,
      a 20-character rationale floor, audited BFF writes, reject/retract, GET
      readback, and reopen behavior.
- [ ] Add a reject action to the `TR-047` editorial artifact lifecycle, with a
      defined policy class and appeal behavior. The adjacent admin-inbox
      decision must not be presented as if it rejects this artifact.

### 8. Publish — target contract

- [ ] An approved artifact is re-evaluated against current citations, policy,
      rights, and target-surface constraints.
- [ ] A distinct, authorized publish action creates the customer-visible
      edition.
- [ ] Generated media receives a verifiable Isis provenance bundle.
- [ ] The target Tara, Veritas, or Nisaba read path returns the new edition.
- [ ] Audit events link submission, automated review, human decisions,
      publication gate, release, and provenance without orphaned records.

The current `in_progress` hotfix stream stops before all five checks above.

## Post-conditions

- For the shipped approve branch, a real release stream exists in `in_progress`
  and its created/transition audit events are queryable.
- For the shipped request-changes branch, a real release stream exists in
  `blocked` with the reviewer rationale.
- The browser exposes the returned stream ID/status and can restore that receipt
  from localStorage.
- No customer publication, provenance bundle, author notification, or complete
  `editorial.*` lifecycle may be inferred from either branch.

## Failure modes

- [x] **Blocked without blocker** — the BFF rejects the mutation with
      `release_stream_invariant_violation`; state and audit count do not change.
- [x] **Mutation after released** — reopening a terminal stream fails with
      `release_stream_invalid_transition`; the closed state remains canonical.
- [x] **Request-changes rationale too short** — the browser blocks submission
      below 20 characters; the server schema also validates its payload.
- [x] **Missing editorial scope** — the release-stream route returns 403
      `admin_scope_missing`.
- [x] **Network/BFF failure** — the decision row renders “Decision did not sync.
      Try again,” keeps the review open, and does not fabricate a receipt.
- [ ] **Double approve** — define an idempotency key or artifact-level
      concurrency rule. The current component can create more than one stream
      after a local reopen.
- [ ] **Approval source drift** — a later source/policy version must invalidate
      stale approval before publication.
- [ ] **Audit/provenance verification failure** — final publish must fail closed
      without affecting unrelated editorial work platform-wide.

## E2E coverage

- [`apps/oshun/web/e2e/operator-studio-surface.spec.ts`](../../apps/oshun/web/e2e/operator-studio-surface.spec.ts)
  pins the current queue/detail anatomy, seven rows, `TR-047` fixture, waveform,
  transcript, notes, citations, decision controls, 44 px rows, and anonymous
  redirect.
- [`apps/oshun/web/e2e/editorial-release-streams.spec.ts`](../../apps/oshun/web/e2e/editorial-release-streams.spec.ts)
  drives real POST/PATCH/audit reads, approve → `in_progress`, request changes →
  `blocked`, receipt restoration, scope denial, invalid blocked mutations, and
  terminal-transition refusal.
- [`apps/oshun/web/e2e/operator-editorial-review-approval.spec.ts`](../../apps/oshun/web/e2e/operator-editorial-review-approval.spec.ts)
  and
  [`apps/oshun/web/e2e/operator-admin-evidence-queue.spec.ts`](../../apps/oshun/web/e2e/operator-admin-evidence-queue.spec.ts)
  cover adjacent admin-inbox decisions and evidence promotion. They do not close
  the missing author-submit or customer-publish legs for `TR-047`.
- **Coverage depth:** partial. The operator decision seam is real over HTTP;
  author ingestion, live queue hydration, automated review, reject on this
  artifact, publication gates, provenance emission, and customer readback are
  not end-to-end.

## Per-view files touched by this journey

- [`studio/studio-overview.md`](../studio/studio-overview.md) — intended author
  entry.
- [`operator/operator-studio.md`](../operator/operator-studio.md) — current
  queue, fixed artifact, notes, citations, and release-stream controls.
- [`operator/operator-admin.md`](../operator/operator-admin.md) — adjacent
  reject/retract decision substrate.
- [`operator/operator-admin-isis-provenance.md`](../operator/operator-admin-isis-provenance.md)
  — intended generated-media proof.
- [`shell/04-auth-session.md`](../shell/04-auth-session.md) — operator gate.
- [`shell/05-notifications.md`](../shell/05-notifications.md) — future
  author-notification handoff.

## Cross-references

- Feature spec: [`V1/features.md`](../../V1/features.md) — Editorial Workflow,
  Sophia publication gates, and Isis provenance bundles.
- Architecture:
  [`V1/ARCHITECTURE.md#operator-surfaces`](../../V1/ARCHITECTURE.md#operator-surfaces).
- Current components: `apps/oshun/web/src/components/lilith/operator.tsx` and
  `apps/oshun/web/src/components/lilith/StudioReviewDecisionRow.tsx`.
- Current data source: `apps/oshun/web/src/lib/lilith-data/operator-depth.ts`.
- Current BFF/audit routes:
  `apps/oshun/bff/src/routes/admin-editorial-release-streams.ts` and
  `apps/oshun/bff/src/routes/admin-audit-log.ts`.

## Open questions / known gaps

- [ ] Define and implement the author-side submit endpoint and its durable
      artifact identity.
- [ ] Replace the fixture queue with BFF-backed items and make row selection
      update the review workspace.
- [ ] Decide whether approve requires free-text rationale and whether “publish
      06:00” should be renamed until scheduling/publication exists.
- [ ] Define reviewer assignment, second-opinion, and audio-QA requirements.
- [ ] Connect automated Lilith/Sophia evidence to a versioned review record.
- [ ] Add an artifact-specific reject/appeal branch.
- [ ] Add the final publication gate, customer-surface write, Isis provenance
      bundle, and cross-surface readback.
- [ ] Make reopen a server transition or relabel it as a local view reset; it
      currently leaves the BFF stream untouched.
