feat(labeler): reconsideration operator console UI (W10.6 client)#2087
Conversation
Operator-facing surface for the reconsideration workflow over the slice-1 server API. Console-only; no server changes. - Reconsiderations list route + case-detail route (header, private note thread oldest-first, Add note, and Resolve while open) modelled on the audit-log/dead-letter list and the AssessmentDetail dialog + invalidateKeys pattern. - Open/Note/Resolve dialogs reuse the shared consoleApiFetch/postAction CSRF + error helpers and the per-open ulid idempotency key, matching the existing action dialogs. Resolve offers granted/denied/withdrawn with inline help that withdrawn sends no notice. - "Open reconsideration" action on AssessmentDetail: attempts the open for the assessment's subject and surfaces the server 409 (an open case already exists) inline — no by-subject lookup added. Resolve of an already-resolved case surfaces its 409 inline too. - Sidebar nav entry, router + test-harness wiring, fixtures, and component coverage (render, dialogs, empty/error/not-found, both 409s, axe, keyboard). Plain-English strings matching the console's existing convention; RTL-safe logical classes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | 1661131 | Jul 17 2026, 06:00 AM |
Scope checkThis PR changes 1,587 lines across 21 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 1661131 | Jul 17 2026, 06:03 AM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-do | 1661131 | Jul 17 2026, 06:01 AM |
There was a problem hiding this comment.
Approach judgment
This is the right next slice. It builds the operator console UI for the W10.6 reconsideration workflow on top of the server API already merged in #2083 (apps/labeler/src/console-api.ts / console-mutation-api.ts). The change is cleanly console-only, reuses the labeler console’s existing seams (consoleApiFetch/postAction, ULID idempotency keys, TanStack Query invalidateKeys, dialog-per-action), and targets the declared integration branch. The routes and dialogs map directly to the endpoints exposed by slice 1, so it fits EmDash’s architecture.
What I checked
- Diff vs. full files: read all changed/new source files, fixtures, tests, and the diff itself; every changed hunk was considered.
- Server/client type alignment: the new console types in
apps/labeler/console/src/api/types.tsmirror the server serializers (serializeReconsideration,serializeReconsiderationNote) and mutation descriptors (ReconsiderationOpenDescriptor,ReconsiderationNoteDescriptor,ReconsiderationResolveDescriptor) field-for-field, including camelCase naming and the envelope shape{ data: T }returned byjsonData. - API envelope & errors:
parseJsonconsumes{ data }; 409 errors fromReconsiderationStateErrorsurface viabody.error.message, matching tests forRECONSIDERATION_OPEN_EXISTSandRECONSIDERATION_RESOLVED.encodeURIComponentis used on route params. - AuthZ gating:
Add note/Resolveon the detail andOpen reconsiderationon the assessment are only rendered whencanAct(reviewer/admin); the server still enforcesrequiredRole: "reviewer". - State/cache/idempotency: idempotency keys are minted fresh per dialog open via
ulid()and reused across retries; cache invalidation (["reconsiderations"],["reconsideration", id]) is awaited before closing/navigating. - AGENTS.md conventions:
- No physical Tailwind directional classes (
ml-,mr-,text-left, etc.) in new files. - The
apps/labeler/consoleSPA has no Lingui setup and uses plain English consistently with the existing console routes; the PR explicitly documents this as out-of-scope for W10.6. @emdash-cms/labelerisprivate: true, so no changeset is required.
- No physical Tailwind directional classes (
- Tests: new unit/route tests plus axe and keyboard-focus coverage for the two reconsideration dialogs; the 409 open-exists and already-resolved paths are both exercised.
Headline conclusion
No blocking issues. The implementation is consistent with the existing console, the server contract, and the stated slice-2 scope.
7c0b753
into
feat/plugin-registry-labelling-service
What does this PR do?
Adds the operator console UI (slice 2) for the W10.6 reconsideration workflow, over the slice-1 server API merged in #2083. Targets the
feat/plugin-registry-labelling-serviceintegration branch. Console-only — no server changes.The workflow is operator-side (no authenticated appeals portal, per the plan). This PR gives operators the surface to run it:
open— a Resolve action (outcome granted/denied/withdrawn + optional final note + required reason). The resolve dialog spells out thatwithdrawnsends no publisher notice.409 RECONSIDERATION_OPEN_EXISTSrenders inline ("an open reconsideration already exists for this subject"). A409 RECONSIDERATION_RESOLVEDon resolve surfaces inline too.All of it reuses the console's existing seams: the shared
consoleApiFetch/postActionhelpers (CSRF header + JSON + credentials), the per-openulid()idempotency key minted the same way as the other action dialogs, the TanStack QueryinvalidateKeys+ dialog-per-action pattern, and the existing mutation-error surface. Client types mirror the slice-1 serializers field-for-field.Part of #1909. Follows #2083.
Type of change
Checklist
pnpm typecheckpasses (including the console project)pnpm lintpasses (0 diagnostics on touched files)pnpm testpasses (136 console tests, up from 100; includes the new routes/dialogs, both 409 paths, axe, and keyboard focus-trap)pnpm formathas been runapps/labeler/console), a separate SPA that is not yet localized (per the W10.6 plan status); it uses plain-English strings matching the existing console routes (AuditLog/DeadLetterQueue/AssessmentDetail). Nopackages/admincatalogs touched. RTL-safe logical Tailwind classes throughout (the console's auto-scanning rtl-safety test covers the new files).@emdash-cms/labelerisprivate: true.AI-generated code disclosure
Screenshots / test output
Try this PR
Open a fresh playground →
A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.
Tracks
feat/labeler-reconsideration-console. Updated automatically when the playground redeploys.