Skip to content

feat(moderation): ✨ report any content record from public pages - #888

Merged
aXenDeveloper merged 2 commits into
canaryfrom
claude/content-report-slice
Oct 19, 2026
Merged

aXenDeveloper merged 2 commits into
canaryfrom
claude/content-report-slice

Conversation

@aXenDeveloper

Copy link
Copy Markdown
Owner

Improving Documentation

  • pnpm lint:fix passes. The one remaining warning, in src/tanstack/layout/route-error.tsx, is already on canary.
  • Docs follow the repository's VitNode docs conventions. All seven edited MDX pages compile.

Description

What?

This is the first part of the Content Engine public-UI plan: reporting, from turning it on for a content type to rendering the button, with docs.

Core (@vitnode/core)

  • New contentTarget(pluginId, definition, id) and the ContentTarget type in @vitnode/core/content. They build the { pluginId, type, id } a public action points at, straight from the content type definition. Content definitions are safe to import in the browser.
  • ReportButton takes target instead of pluginId, targetType and targetId. Accessibility changes:
    • Its accessible name is now "Report {type}".
    • It sets aria-haspopup="dialog".
    • A new optional describedBy lets a list of buttons be told apart, for example by the comment's author.
    • While messages load it shows a skeleton instead of an unnamed disabled button.
  • New export ReportDialog: the same dialog, controlled by the caller, for a custom trigger or for one dialog shared by a long list.
  • REPORT_NAMESPACES is now exported.
  • A 401 while reporting, for example when the session expired, shows a "sign in again" toast instead of the generic error.

Plugins

  • Forum:
    • reports is enabled on forum.topic and forum.comment. Comment snapshots keep the body.
    • Everyone except the author gets a Report action on topics and comments. The comment button is described by the author's name.
    • The generated report and hidden-view permissions get forum labels.
  • Blog: switched to target.

Docs

  • dev/moderation/enable-reports.mdx:
    • Uses the new API.
    • Shows where the identifiers come from.
    • Adds a "Report items in a list" section using forum comments.
  • dev/moderation/reference.mdx:
    • New "Report UI" section with contentTarget, ReportButton and ReportDialog.
    • Notes that report permissions stay unscoped for content whose container comes through a relation, such as comments → topic.
  • Fixes to existing pages:
    • threads.mdx: relations are { id }, and the snippet now matches the real fetchThreadPage.
    • approvals.mdx: removed a reference to a non-existent file.
    • public-write-api.mdx: the content-write rate limit counts per user when signed in.
    • public-write-frontend.mdx: new section on approvals and edit windows.
    • content-engine/reference.mdx: links the report UI.

Breaking (canary): ReportButton's pluginId, targetType and targetId props are replaced by target.

<ReportButton
  describedBy={authorId}
  target={contentTarget(manifest.id, commentContentType, comment.id)}
  typeLabel={t("report_type")}
/>

Why?

The backend and ReportButton already existed, but:

  • Nothing showed how to report a list item such as a forum comment.
  • The reference plugin (forum) didn't enable or render reports.
  • Every button in a list had the same accessible name.
  • Callers passed three separate identifiers that could drift from the registered content type.

This slice sets the pattern the later phases will reuse: one target identifier, a ready-made component plus a controlled primitive, real plugin usage, and a task-focused doc.

Verification

  • Core typecheck passes. Core, forum and blog build with type checking.
  • Forum tests pass, plus 19 related core test files (229 tests).
  • Checked in a running app against a fresh database:
    • Bob sees "Report topic" and "Report comment" (described by "alice"). Sending a report shows "Report sent", and sending it again shows the duplicate toast. Focus returns to the trigger.
    • Alice sees Edit and no Report button.
    • A guest gets a Sign in link with returnTo.
    • The report row is stored with targetType = forum.comment and a body snapshot.
  • Regression tests are being written in a separate session, as AGENTS.md requires, and will land on this branch.

Known limits, left for later

  • A comment report's title is its generated slug, because comments have no title. Moderators still see the body.
  • Scoping comment reports per forum section needs :tree-aware report filtering.

Generated by Claude Code

- Add `contentTarget` and `ContentTarget` to `@vitnode/core/content` so a
  public action names its record from the content type definition.
- `ReportButton` takes `target`, gets an accessible name ("Report {type}"),
  `aria-haspopup`, an optional `describedBy`, and a non-focusable skeleton
  while its messages load.
- Export `ReportDialog` (controlled) and `REPORT_NAMESPACES`.
- An expired session (401) now shows a "sign in again" toast.
- Forum: enable reports on topics and comments, render the Report action for
  everyone but the author, and label the generated permissions.
- Blog: switch to `target`.
- Docs: list-item reporting, `ReportButton`/`ReportDialog` reference, and
  fixes in threads, approvals, public write and rate limit pages.

BREAKING CHANGE: `ReportButton` takes `target` instead of `pluginId`,
`targetType` and `targetId`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CE6w3pVi8mgjZMhax7f6tu
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T22:53:55.794112Z 5a0a275 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the 💡 Feature A new feature label Oct 18, 2026
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 5a0a275.

- ReportButton: accessible name, aria-haspopup, aria-describedby, opening
  the dialog, and the guest sign-in link with returnTo.
- ReportDialog: renders the dialog only when open.
- POST /reports: 401 for guests, 400 for a short "other" description, and
  429 rate_limited until the hour passes.
- contentReportTarget capture: snapshot of a published record, nothing for
  hidden, draft or missing ones.
- createReport: maps 401 to "unauthenticated" alongside the other failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0127YWdeYvCW1buERsEUUMbu

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a0a2758a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


publication: true,
visibility: true,
reports: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Scope topic reports to their forum

Configure the topic model's report target with the forum column and forumContainers.id as its moderation scope. topicContent is currently created without moderationScope, so enabling reports here generates unscoped can_view_reports, can_manage_reports, can_view_hidden, and can_hide permissions: a moderator intended to handle one forum can consequently view and act on topic reports from every forum, unlike the existing container-scoped topic edit and approval permissions.

Useful? React with 👍 / 👎.

Comment on lines +39 to +40
definition: { id: string },
id: ContentId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Tie target IDs to the definition's ID strategy

Make this helper generic over the content definition and accept ContentIdOf<TDefinition> rather than the unconstrained ContentId. As written, a UUID definition accepts a number and a bigint definition accepts a JavaScript number; the former produces a target the server rejects, while a bigint beyond Number.MAX_SAFE_INTEGER can be rounded before stringification and report the wrong record. The rest of the Content Engine already models bigint and UUID IDs through ContentIdOf specifically to prevent this mismatch.

Useful? React with 👍 / 👎.

@aXenDeveloper
aXenDeveloper merged commit 1f46677 into canary Oct 19, 2026
6 checks passed
@aXenDeveloper
aXenDeveloper deleted the claude/content-report-slice branch October 19, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💡 Feature A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants