Skip to content

feat(labeler): reconsideration operator console UI (W10.6 client)#2087

Merged
ascorbic merged 1 commit into
feat/plugin-registry-labelling-servicefrom
feat/labeler-reconsideration-console
Jul 17, 2026
Merged

feat(labeler): reconsideration operator console UI (W10.6 client)#2087
ascorbic merged 1 commit into
feat/plugin-registry-labelling-servicefrom
feat/labeler-reconsideration-console

Conversation

@ascorbic

@ascorbic ascorbic commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

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-service integration 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:

  • Reconsiderations list route — keyset-paginated cases with subject, state, outcome, and open/resolve provenance; rows link to the case detail.
  • Case detail — the header (subject, state, outcome, triggering assessment), the private note thread (oldest-first, attributed), an Add note action, and — while the case is open — a Resolve action (outcome granted/denied/withdrawn + optional final note + required reason). The resolve dialog spells out that withdrawn sends no publisher notice.
  • "Open reconsideration" on the assessment detail view — opens a case for that assessment's subject. There is no by-subject lookup API, so the UX is attempt-and-surface: a 409 RECONSIDERATION_OPEN_EXISTS renders inline ("an open reconsideration already exists for this subject"). A 409 RECONSIDERATION_RESOLVED on resolve surfaces inline too.

All of it reuses the console's existing seams: the shared consoleApiFetch/postAction helpers (CSRF header + JSON + credentials), the per-open ulid() idempotency key minted the same way as the other action dialogs, the TanStack Query invalidateKeys + 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

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes (including the console project)
  • pnpm lint passes (0 diagnostics on touched files)
  • pnpm test passes (136 console tests, up from 100; includes the new routes/dialogs, both 409 paths, axe, and keyboard focus-trap)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable) — n/a: this is the labeler operator console (apps/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). No packages/admin catalogs touched. RTL-safe logical Tailwind classes throughout (the console's auto-scanning rtl-safety test covers the new files).
  • I have added a changeset (if this PR changes a published package) — n/a: @emdash-cms/labeler is private: true.
  • New features link to an approved Discussion — the umbrella Plugin registry labelling service #1909 tracks RFC RFC: Decentralized Plugin Registry #694; this is one slice under it.

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.8 (implementation), orchestrated + reviewed by Claude Fable 5

Screenshots / test output

Test Files  15 passed (15)
     Tests  136 passed (136)

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.

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>
@changeset-bot

changeset-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 1661131

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache 1661131 Jul 17 2026, 06:00 AM

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This 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.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground 1661131 Jul 17 2026, 06:03 AM

@github-actions github-actions Bot added the review/needs-review No maintainer or bot review yet label Jul 17, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-do 1661131 Jul 17 2026, 06:01 AM

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ts mirror the server serializers (serializeReconsideration, serializeReconsiderationNote) and mutation descriptors (ReconsiderationOpenDescriptor, ReconsiderationNoteDescriptor, ReconsiderationResolveDescriptor) field-for-field, including camelCase naming and the envelope shape { data: T } returned by jsonData.
  • API envelope & errors: parseJson consumes { data }; 409 errors from ReconsiderationStateError surface via body.error.message, matching tests for RECONSIDERATION_OPEN_EXISTS and RECONSIDERATION_RESOLVED. encodeURIComponent is used on route params.
  • AuthZ gating: Add note/Resolve on the detail and Open reconsideration on the assessment are only rendered when canAct (reviewer/admin); the server still enforces requiredRole: "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/console SPA 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/labeler is private: true, so no changeset is required.
  • 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.

@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-review No maintainer or bot review yet labels Jul 17, 2026
@ascorbic ascorbic mentioned this pull request Jul 17, 2026
81 tasks
@ascorbic
ascorbic merged commit 7c0b753 into feat/plugin-registry-labelling-service Jul 17, 2026
11 checks passed
@ascorbic
ascorbic deleted the feat/labeler-reconsideration-console branch July 17, 2026 06:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: signed review/approved Approved; no new commits since size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant