Conversation
🦋 Changeset detectedLatest commit: 6ebf5b4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 18 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Scope checkThis PR changes 1,993 lines across 59 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 |
docs | 03c2e00 | Sep 17 2026, 12:24 PM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-test
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-loader
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
🚀 Deploying Preview to Cloudflare 🚀Preview URL: https://codex-comment-administration.try.emdashcms.com, https://codex-comment-administration-emdash-playground.emdash-cms.workers.dev (commit 6ebf5b4)This URL reflects your latest Preview deploymentPreview Deployments by commit
|
There was a problem hiding this comment.
The approach is sound and well-scoped: it extends EmDash’s existing capability model to comments with clear separation between read (comments:read) and moderation (comments:moderate, which implies read), uses expected-status preconditions for safe concurrent moderation, and carries the same contract through native plugin contexts, the Cloudflare sandbox bridge, the workerd bridge, manifest/lexicon declarations, and admin consent labels. It fits the existing architecture without touching logged-out hot paths.
I traced the core path end-to-end: CommentRepository.updateStatusIf correctly atomically guards on the expected status; EmDashRuntime fences recursive and overlapping moderation through both an AsyncLocalStorage guard and an in-progress set; the admin route delegates through handleCommentModerate with the observed previous status so admins keep the same restore-from-trash behavior; and both the Cloudflare PluginBridge and workerd bridge-handler validate plugin-provided statuses before touching the runtime. The plugin-test host, bridge tests, and repository tests cover status conflicts, recursion blocking, personal-data shape, and runtime bridge round-trips.
I found only two small issues: a stale comment that no longer matches the awaited behavior, and a changeset paragraph that describes an internal test helper rather than user-facing release impact.
There was a problem hiding this comment.
Re-review of the sandboxed comment-administration slice.
Approach judgment: The change is still the right, well-scoped capability extension. It gives plugins explicit, consent-gated comments:read/comments:moderate access, keeps moderation behind an observed-status precondition, and routes both plugin and admin transitions through the same core service so notifications and hooks behave identically. The architecture matches the existing content/media capability model and does not touch logged-out routes.
What I checked:
- Read the full diff and the prior review context.
- Verified the two prior findings are addressed: the
comment:afterModeratehook contract and comments now describe the awaited (not fire-and-forget) behavior, and the changeset now describes the user-facing release impact instead of an internal test helper. - Traced the moderation path end-to-end:
CommentRepository.updateStatusIfatomically guards onexpectedStatusand returns clearupdated/unchanged/conflict/not_foundstates.EmDashRuntimefences recursion withAsyncLocalStorageand overlaps with a per-runtimecommentModerationInProgressset, returningCOMMENT_MODERATION_IN_PROGRESSorCOMMENT_STATUS_CONFLICTas appropriate.- The admin route delegates through
handleCommentModeratewith the observed prior status, preserving restore-from-trash behavior, and still checkscomments:moderateauthorization. - Plugin contexts, the Cloudflare
PluginBridge, the workerdbridge-handler, and both generated wrappers validatecomments:moderatevscomments:readand canonicalizecomments:moderate⇒comments:read.
- Checked that
PluginCommentomitsauthorUserIdwhile exposing the documented personal-data fields, and that documented shapes/capabilities match code and tests. - Reviewed SQL usage in the changed repository code: Kysely builders, no raw interpolation, existing
idx_comments_statusindex covers the new status filters. - Confirmed the changeset package list matches the changed packages and that the prose meets the
.changeset/README.mdguidance.
Headline conclusion: This is a clean, complete vertical slice. The prior blockers are resolved, and I found no new logic bugs, security issues, or AGENTS.md convention violations. LGTM.
What does this PR do?
Adds the complete sandboxed comment-administration vertical slice with
comments:readandcomments:moderate. Moderation implies read access.ctx.commentscan get, count, and cursor-page through non-trashed comments. The read capability explicitly exposes author names and email addresses, comment bodies, pseudonymous IP hashes, user agents, and moderation metadata, but not linked EmDash user-account IDs. Installation and updates surface that personal-data access for consent.Authorized plugins can move comments between
approved,pending, andspamthrough the core moderation service. Each mutation requires the previously observed status, rejects stale writes withCOMMENT_STATUS_CONFLICT, identifies plugin origin, runscomment:afterModerateonce, preserves approval notifications, and fences recursive moderation across native and sandbox execution. Hard deletion and bulk replacement remain out of scope.The contract is carried through shared and core manifest validation, declared-access canonicalization, registry lexicons and generated types, the plugin CLI and generated authoring skill, native context, Cloudflare Worker Loader, Node/workerd, and runtime-backed plugin-test helpers. Public documentation and package changesets describe the same authority and result shape.
This is an independent capability from the maintainer-directed sandbox capability plan, built on the runtime-test foundation merged in #3162.
Type of change
This is maintainer-directed capability work; there is no separate Discussion URL.
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (targeted tests for this change)pnpm formathas been runmessages.pofiles included)AI-generated code disclosure
Screenshots / test output
Not applicable; this PR does not change rendered UI.
Validated locally:
git diff --check;A separate adversarial-review agent reviewed the exact
main...6ebf5b4988dea0a399434b7bb0690713e54dd1f4diff. Its three findings were fixed and the exact-SHA re-check passed with no remaining blockers.