Organizer rehearsal review → confirm workflow wireframe (interactive)

Purpose

This wireframe defines the interactive organizer surface for the no-CLI organizer/member rehearsal workflow: review proposed work, request a bounded edit, ask for more information, reject, assign by registered label, preview the exact mutation a node would make, and confirm it — creating one real action item and its ADR-0026 receipt ladder on an isolated Rehearsal Node, which the assigned member then completes on the existing member surface.

It is rendered by the canonical browser client web/member-shell/ behind an explicit ?surface=organizer selector, live-mode only, against a gateway started in Rehearsal mode (ICN_GOVERNANCE_BUILD_MODE=rehearsal). It drives the merged runtime contract in `rehearsal-review-workflow.md` without inventing endpoint shapes.

Relationship to the read-only preview wireframe

This document extends — it does not replace — `ORGANIZER_REVIEW_PREVIEW_WIREFRAME.md`. That wireframe remains the honest record of the first slice: a read-only, fixture-backed ?mode=demo preview whose review affordances are real disabled buttons that record nothing (PR #2237). Nothing in this document retcons that one into a mutation surface.

The change since then is that the runtime now exists (PR #2406): a Rehearsal-mode-only gateway surface that records real decisions, binds a preview digest to a confirmation, walks the real receipt ladder, and creates one real action item. This wireframe describes the browser rendering of that runtime — with enabled controls whose consequences are shown before any confirm — and is the design of record for the browser slice (PR 1 of the browser/appliance tranche). The read-only doc's disabled-affordance frames still describe the demo surface, which is unchanged.

Why ?surface=organizer, live-only

The existing member-shell grain is ?mode=(demo|live) plus a demo-only ?set= fixture selector plus resilient section toggles for live data. The organizer workflow is a whole role view with its own multi-step state machine, its own credential, and its own capabilities. The smallest honest seam is one new, explicit role selector — ?surface=organizer — that is absent by default (the member surface is the default) and is honoured only in live mode, because the workflow requires a running Rehearsal-mode node and a review/confirm credential. There is no fixture "organizer demo": a fixture surface that looked like it confirmed work would be fake success. Demo mode therefore keeps its fixture-only, non-mutating guarantee, and ?mode=demo&surface=organizer exposes no mutation controls. Accessibility and behaviour are exercised against the live surface with Playwright route interception (real DOM, real wire shapes, no real node).

Authority boundary (make it visible and semantic)

The surface must express three distinct authority levels; the organizer browser credential is the narrow middle band and never holds the outer two:

setup / steward authority (internal credential — NOT in the browser):
  initialize the first workspace for a domain (designation)
  bind a registered label to a fictional identity (holds a DID)

organizer authority (the browser credential in this PR):
  read · review · bounded edit · assign an existing label · preview · confirm

member authority (a separate credential / the member surface):
  read · complete their assigned action item

The organizer surface never initializes a workspace, never binds a label to an identity, and never handles, accepts, infers, displays, or transmits a DID. Unbound labels are shown as "needs steward setup"; the browser cannot fix that. This mirrors the runtime scopes: the browser credential carries governance:read, governance:pending-publish:review, and governance:pending-publish:confirm — never governance:rehearsal:setup, governance:write, governance:action-item:complete, governance:meeting:write, entity:write, or coop:admin.

Runtime contract binding (screen → route)

All paths are under the gateway's /v1/gov mount. {d} is the selected domain.

Screen / action Method + route Scope used
Standing / eligible domains GET /me/standing governance:read
Proposed-work list GET /domains/{d}/rehearsal/pending-publish governance:read
Row detail GET /domains/{d}/rehearsal/pending-publish/{row} governance:read
Registered labels GET /domains/{d}/rehearsal/bindings governance:read
Review decision POST …/{row}/review pending-publish:review
Bounded edit (plain_summary ≤256B) PUT …/{row} pending-publish:review
Assign / clear label POST …/{row}/assign pending-publish:review
Exact mutation preview GET …/{row}/preview pending-publish:review
Confirm (digest only) POST …/{row}/confirm pending-publish:confirm
Receipts read-back GET /domains/{d}/rehearsal/receipts governance:read
Evidence packet GET /domains/{d}/rehearsal/evidence-export governance:read

The organizer surface never calls POST …/rehearsal/bindings (setup) or POST …/rehearsal/reset (steward/designation). Those are the internal credential's.

Human sequence

Open ?surface=organizer (live) → connect with the organizer credential
  → see standing and the permanent Rehearsal-mode boundary
  → select a domain (explicit choice when standing shows more than one)
  → inspect proposed work (list → one row)
  → review: approve / reject / request an edit / ask for more information
  → where appropriate, edit the one allowlisted summary field
  → assign by registered label (or leave/clear)
  → preview exactly what the node will create
  → confirm the bound preview (a separate, explicit screen)
  → see the created action item and the process receipts
  → continue as the assigned member (member surface) to complete the card
  → inspect the completion receipt and the privacy-safe evidence summary

Frames

Frame 0 — entry, connect, permanent Rehearsal boundary

┌──────────────────────────────────────────────────────────────────┐
│ ICN Member Shell — Organizer rehearsal                            │
│ REHEARSAL MODE · fictional data · not a pilot, not live federation │  ← text banner, always visible
│                                                                    │
│ Connect to a local Rehearsal-mode node                             │
│ Gateway address [http://localhost:8080          ]                  │
│ Organizer credential [ •••••••••••••••••••••• ]                    │
│   Needs governance:read, governance:pending-publish:review, and    │
│   governance:pending-publish:confirm. It must NOT carry setup,     │
│   write, or completion authority, and never contains a DID.        │
│ [ Connect and load the rehearsal workspace ]                       │
└──────────────────────────────────────────────────────────────────┘

Required behavior: the Rehearsal boundary is text, not a color-only badge, and is present before any control. The credential is captured into page memory only and the input is blanked immediately after capture (existing discipline); it is transmitted only as an Authorization header to the entered gateway.

Frame 1 — domain selection

┌──────────────────────────────────────────────────────────────────┐
│ Which domain are you rehearsing in?                                │
│ ( ) Example Working Group   (member)                               │
│ ( ) Example Stewards Circle  (member)                              │
│ [ Open workspace ]                                                 │
└──────────────────────────────────────────────────────────────────┘

Derive eligible domains from standing.domains[]. Auto-select when exactly one is eligible; require an explicit choice when more than one. Zero eligible → plain explanation ("you are not a member of a domain with a rehearsal workspace"), standing still shown.

Frame 2 — workspace not initialized (404)

┌──────────────────────────────────────────────────────────────────┐
│ This domain's rehearsal workspace has not been set up yet.         │
│ A steward initializes the fictional workspace using an internal    │
│ setup credential. This organizer view cannot create it.            │
│ [ Check again ]                                                    │
└──────────────────────────────────────────────────────────────────┘

On GET …/pending-publish → 404: explain; do not silently call reset; do not ask for or acquire setup authority; keep standing usable.

Frame 3 — proposed-work list → one row

┌──────────────────────────────┬───────────────────────────────────┐
│ Proposed work (generation 1) │ Draft a sample agenda for the next │
│ 1 Action item · Awaiting rev.│ organizing cycle.                  │
│ 2 Decision · Needs more info │ Kind: Action item   Status: Await… │
│ 3 Attendance · Approved      │ Scope: Example Working Group        │
│                              │ Governing body: Example Stewards   │
│                              │ Assignee label: Example member      │
│                              │   (label only — not an identity)    │
│                              │ Authority: assigned action item     │
│                              │ Risk: ○ Low   Expected: completion │
│                              │ Source: committed fixture           │
└──────────────────────────────┴───────────────────────────────────┘

Single-column on narrow screens (list precedes the selected row). The selected row is an <article> with a heading; the list is a semantic list. Status, risk, and origin always carry words + glyph, never color alone. Assignee is always Assignee label, never DID.

Frame 4 — review, edit, assign

┌──────────────────────────────────────────────────────────────────┐
│ Review this proposed work                                          │
│ [ Approve for the next step ] [ Reject ]                           │
│ [ Request an edit ] [ Ask for more information ]                   │
│ Note (optional, ≤2000) [__________________________________]        │
│ Approving does not by itself publish or create anything.           │
│                                                                    │
│ Edit the summary (only field you can change, ≤256 characters)      │
│ [ Draft a sample agenda for the next organizing cycle.        ]    │
│ Editing returns this to “awaiting review” and clears any prior     │
│ approval and preview.                                              │
│                                                                    │
│ Assign to a registered label                                       │
│ [ Example member ▼ ]  (bound)   [ Clear assignment ]               │
│ An unbound label needs steward setup before you can confirm.       │
└──────────────────────────────────────────────────────────────────┘

Only the four runtime decisions are offered (approve, reject, needs_edit, needs_more_info), rendered as the plain labels above; the raw value lives in a data-* attribute for tests, never in copy. Editing targets only plain_summary (≤256 bytes) — no raw-JSON or hidden-field editing. Assignment lists registered labels from the bindings route with a bound/unbound flag; the organizer may select or clear an existing label, never create or rebind one, and never sees a DID. Any review/edit/assignment invalidates a prior preview.

Frame 5 — exact mutation preview

┌──────────────────────────────────────────────────────────────────┐
│ Preview: what confirming will create                              │
│ Action: Create one action item on this Rehearsal Node             │
│ Title:        Draft a sample agenda for the next organizing cycle │
│ Description:  <generated plan description>                         │
│ Domain:       Example Working Group                               │
│ Assignee:     Example member (label, bound)                       │
│ Authority basis: assigned action item                             │
│ Risk:         ○ Low                                               │
│ Expected receipts: gate result · activation · plan · applied      │
│ Reversible:   No — creates a real item and permanent receipts     │
│ Creates a real action item: Yes                                   │
│ Privacy: value-withheld — identities appear only as labels        │
│ ▸ Technical details (digest, ids)                                 │
│ [ Continue to confirm ]  [ Cancel ]                               │
└──────────────────────────────────────────────────────────────────┘

The preview is a pure GET; it is requested only when the row is currently approved and its assignment is valid. Render every human-relevant mutation field returned by the runtime (title, description, domain_id, assignee_label + bound flag, authority_basis, risk_level, receipt_expected/expected_receipts, reversible = No, permanence_note, privacy_note, confirmable, and that a real action item is created). The 64-hex preview_digest lives under Technical details only; the client never recomputes or reinterprets it. confirmable: false (unbound assignee) disables Continue with a plain reason.

Frame 6 — confirm (separate, explicit)

┌──────────────────────────────────────────────────────────────────┐
│ Confirm this rehearsal mutation                                    │
│ You are about to create one real action item and permanent process │
│ receipts on this isolated Rehearsal Node. Fictional data; not a    │
│ pilot; receipts record process facts and grant no authority.       │
│ [ Confirm and create the action item ]   [ Go back ]               │
└──────────────────────────────────────────────────────────────────┘

Confirmation is a separate screen from preview. It sends only the preview_digest. The Confirm button disables itself on submit so one interaction cannot emit two confirm requests. Success (201 first / 200 idempotent replay both read as success):

┌──────────────────────────────────────────────────────────────────┐
│ ✓ Created one action item on this Rehearsal Node                   │
│ It is now assigned to “Example member” and appears as an action    │
│ card. Receipts below record what happened; they grant no authority.│
│ ▸ Technical details (action_item_id, ladder ids + hashes)          │
│ [ View receipts ]  [ View evidence summary ]                       │
│ [ Continue as the assigned member ]                                │
└──────────────────────────────────────────────────────────────────┘

An idempotent replay is rendered honestly ("this preview was already confirmed; nothing new was created").

Frame 7 — interrupted / stale preview

A stale preview (409) clears the local preview and returns the organizer to the refreshed row with: "The proposed work changed since this preview was made. Review the current version and preview again." No Confirm button remains enabled across a stale state. An interrupted execution (a pending item recorded but the applied receipt not yet written) is surfaced as an honest partial state that an identical retry resumes — never as a second item and never as false success.

Frame 8 — receipts and evidence

Receipts (GET …/receipts) list the ladder classes with ids + hashes only (no DIDs), each in plain language ("Process session opened", "Gate result", "Activation crossed", "Mutation plan recorded", "Mutation applied", plus the per-row decisions). Evidence (GET …/evidence-export) renders the value-withheld packet summary: per-row outcome, decision log with real receipt hashes, run/generation, binding flags (no DIDs), the privacy-review result, and the non-claims. Both packet hashes (packet_hash BLAKE3 + packet_hash_sha256) are shown under technical detail and described as server-computed and independently verifiable by a steward; the browser does not claim to have verified them (steward/appliance cryptographic verification is the next slice).

Frame 9 — continue as the assigned member

"Continue as the assigned member" navigates to the member surface (drop ?surface=organizer) where the member connects with a member credential (governance:read + governance:action-item:complete) and completes the card on the existing, unchanged completion flow. In this PR the role transition is a navigation with a fresh credential entry; the appliance PR replaces manual entry with a fresh least-privilege local session (never a token upgrade).

State machine

A single organizer workflow state (not scattered booleans), guarded by a monotonic request generation so an abandoned response can never render into a newer connection/domain/row:

disconnected → loading-standing → domain-selection → loading-workspace
  → workspace-uninitialized (404, terminal-until-retry)
  → row-list → row-selected → editing → review-submitting → preview-ready
  → confirming → confirmed
                     ↘ stale (409 → back to row-selected, preview cleared)
                     ↘ error (surfaced, recoverable)

Invariants:

  • changing the domain clears prior rows, preview, and selection;
  • changing the selected row clears the prior preview;
  • any edit / review / assignment clears the prior preview;
  • a control is disabled during its own in-flight mutation;
  • a stale or failed request can never leave an enabled Confirm button;
  • one interaction never emits two confirm requests;
  • a new or failed connection attempt never retains a previous organizer's rows.

Implemented with a monotonic organizerSeq (mirroring the existing liveLoadSeq pattern) and/or AbortController; every async continuation bails when its captured generation is stale.

Error states (organizer-facing copy, not raw codes)

Status Meaning shown to the organizer
401 Your session is unavailable or expired — connect again.
403 This credential lacks the authority for that step.
404 The workspace is not set up, or that item is no longer available.
409 The item changed (stale preview / conflict / pending recovery / reassignment) — review the refreshed state.
422 That label is not bound, or this kind of work cannot be confirmed here.
500 The node could not complete the request — nothing was assumed done.

Raw backend detail goes to the console, never as the primary surface (existing liveFetch discipline). A stale 409 clears the local preview.

Accessibility & i18n (in scope for the 12-category gate)

Every new string uses the i18n catalog (t() / data-i18n), preserving the pseudo-locale (?lang=qps-ploc) and RTL (?lang=ar). DOM is built only with createElement/textContent (no dynamic innerHTML), native controls, visible focus, logical keyboard order, ≥44×44 targets, 200% zoom reflow, a 375px single-column layout with no horizontal scroll, and reduced-motion behavior; no meaning by color alone. After mutations, focus moves to the resulting heading / status summary and restrained aria-live announces the outcome; errors and help are associated with the responsible control; the consequence text is available before Confirm.

An automated axe/Playwright pass does not close #2041. The 12-category accessibility gate categories that require a human assistive-technology pass (3.2 screen reader, 3.9 AT + non-mouse input, 3.3 human 200% zoom + contrast tool, 3.5 switch control) are marked "Pass with documented follow-ups → #2041" in the PR, never "Pass".

Credential handling (this PR)

Manual organizer credential entry, memory-only: held only in the in-page state object, never localStorage/sessionStorage/cookies/IndexedDB/URL/service-worker, blanked from the input after capture, never displayed after submission, never logged, sent only to the entered gateway as a Bearer header, and dropped on a new or failed connection attempt. A later appliance PR removes manual entry from the normal assembled walkthrough in favor of a fresh least-privilege local session.

Implementation boundary (PR 1)

In scope: the ?surface=organizer live surface in web/member-shell/ driving the merged runtime routes above; the organizer workflow state machine; manual memory-only credential entry; full i18n + accessibility; Playwright route-interception behavioral + axe coverage; this wireframe.

Out of scope (later PRs, explicitly): appliance/session wiring and the one-command assembled loop; a real assembled-image witness; removing manual credential entry; NYCN packaging or facilitator materials; the human assistive-technology pass; post-merge truth-sync. The browser reuses the runtime contract unchanged; if a runtime defect is found, the smallest correction is isolated in its own commit/PR, not folded into the browser change.

Non-claims

Carried verbatim from the runtime contract (`rehearsal-review-workflow.md`):

Not production governance, not a pilot, not live federation, not durable workflow storage (the review workspace is node-lifetime and resettable; the created items and receipts are the durable records). Identity binding here is a fictional-rehearsal convenience, not the production private-overlay architecture.

And for this browser slice specifically:

  • Not an organizer-ready, organizer-validated, or accessibility-validated surface; automated accessibility is not a human assistive-technology pass.
  • Not a production or pilot deployment, not live federation, not private-data handling.
  • The browser never handles a DID and never holds setup, write, completion, or admin authority.
  • Receipts record process facts and grant no authority; a successful software rehearsal is not organizer approval.
  • Does not complete #1726, #1728, #2386, #1746, or #2041 — the human gates (#2041 assistive technology, #1703 organizer presentation / first operator rehearsal, #1746 operable-rehearsal milestone) stay open.