Skip to content

feat(notebooks): require feed tile summary when leading markdown is noisy#4761

Open
leonardthethird wants to merge 3 commits into
mainfrom
feat/notebook-feed-tile-summary-validation
Open

feat(notebooks): require feed tile summary when leading markdown is noisy#4761
leonardthethird wants to merge 3 commits into
mainfrom
feat/notebook-feed-tile-summary-validation

Conversation

@leonardthethird

@leonardthethird leonardthethird commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a conditional validation rule to the notebook create/edit form: if the first ~250 characters of the notebook's markdown contain formatting tokens that the feed tile cannot cleanly render as plain text, feed_tile_summary becomes a required field, with a red inline explanation under the field telling the author why.

Motivation

Notebook feed tiles fall back to getMarkdownSummary(markdown, ...) when feed_tile_summary is empty. That helper runs strip-markdown with keep: ["link"], then walks tokens with a regex that preserves inline code as well. The result: notebooks whose openings include links, inline / fenced code, raw HTML, images, or tables render with visible syntax on feed cards (e.g. [click here](https://...), backticks, <iframe ...>). Authors then have to discover the Feed Tile Summary field on their own to clean it up after the fact.

This shifts the discovery to creation time. The author is told, at submit, exactly why the summary is required and what to do.

Implementation

  • front_end/src/app/(main)/questions/components/notebook_form.tsx
    • New FEED_TILE_PREVIEW_CHAR_LIMIT = 250 constant (conservative upper bound; actual tile budget is width/height-derived at render time).
    • New NOISY_MARKDOWN_PATTERNS list of regexes: images, links, inline code, fenced code, HTML tags, table rows. Plain emphasis (**bold**, *italic*) is intentionally excluded since strip-markdown cleans those up on the tile.
    • createNotebookSchema now wraps its z.object({...}) in a .superRefine() that adds a ZodIssueCode.custom issue with path: ["feed_tile_summary"] when the markdown preview is noisy and the summary is empty / whitespace-only. The existing Textarea already renders field errors as red text via FormError.
  • front_end/messages/en.json
    • New i18n key feedTileSummaryRequiredForFormattedContent with the explainer text shown under the field.

Server-side validation was intentionally skipped — this is a UX rule, not a data integrity rule, and the existing notebook write serializer has no equivalent cross-field validation pattern.

Test plan

  • Create a notebook with a clean plain-text opening — form submits without requiring Feed Tile Summary.
  • Create a notebook whose first paragraph contains a markdown link ([text](url)) — submit blocked, red explainer text appears under Feed Tile Summary.
  • Repeat with: inline code `foo`, fenced code block, raw <iframe> tag, image syntax ![](...), and a markdown table — each should trigger the requirement.
  • Fill in Feed Tile Summary in each case — form submits successfully.
  • Edit an existing notebook with noisy leading markdown but no summary — form correctly requires the summary on save.
  • Confirm field-level error renders red (matching existing form_field styling).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements

    • Notebook form now requires a short plain-text feed tile summary when the notebook’s visible start contains formatted content (e.g., tables or raw HTML) that wouldn’t render cleanly on a feed; the form will prompt and validate that summary.
  • Localization

    • Added an English message clarifying the feed tile summary requirement for formatted content.

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds detection of "noisy" notebook markdown previews (raw HTML or table-like rows) and a Zod cross-field validation that requires a non-empty feed_tile_summary when the truncated preview matches those patterns; also adds an English i18n message key for the validation error.

Changes

Feed Tile Summary Validation for Formatted Content

Layer / File(s) Summary
Noisy tile preview detection
front_end/src/app/(main)/questions/components/notebook_form.tsx
Adds import of getMarkdownSummary, preview sizing constants, raw-source and rendered-preview regex patterns, and hasNoisyTilePreview which renders a truncated markdown preview and checks for raw HTML tags or table-like pipe rows.
Feed tile summary cross-field validation
front_end/src/app/(main)/questions/components/notebook_form.tsx
Adds a Zod superRefine to createNotebookSchema that trims feed_tile_summary and, if empty when the preview is noisy, reports a Zod issue on feed_tile_summary using the new translation key.
English localization entry
front_end/messages/en.json
Inserts feedTileSummaryRequiredForFormattedContent near feedTileSummaryPlaceholder with guidance that a short plain-text summary is required when the visible start contains a table or raw HTML.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Metaculus/metaculus#4113: Earlier PR that added the feed_tile_summary field and UI wiring used by this cross-field validation.

Suggested reviewers

  • hlbmtc

Poem

A rabbit hops through markdown streams,
Sees pipes and tags that break the dreams,
"Give me plain words, short and bright," it pleads,
So feed tiles show the clearest leads. 🐇✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a requirement for feed tile summaries when leading markdown contains formatting that doesn't render cleanly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/notebook-feed-tile-summary-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Cleanup: Preview Environment Removed (Stale)

This preview environment has been deleted because the PR was marked as Stale (no activity detected).

Resource Status
🌐 Preview App Deleted
🗄️ PostgreSQL Branch Deleted
⚡ Redis Database Deleted
🔧 GitHub Deployments Removed
📦 Docker Image Retained (auto-cleanup via GHCR policies)

Push a new commit to this PR to recreate the preview environment.


Automated cleanup by weekly maintenance

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@front_end/src/app/`(main)/questions/components/notebook_form.tsx:
- Around line 272-276: Remove the temporary debug console logging calls in the
notebook_form.tsx onValid handler (the console.log("[FEED_TILE_DEBUG] onValid ->
submitting", ...) lines and the similar debug log at 279-281); delete these
console statements so no console.debug/console.log remains in the onValid
submission path (keep all functional logic unchanged).
- Around line 93-101: Temporary diagnostic console logging inside the
superRefine validation (the console.log call logging "[FEED_TILE_DEBUG]
superRefine fired" and fields like data.markdown, summary, noisy, willAddIssue)
must be removed before merge; delete the console.log statement (or replace it
with a dev-only debug logger behind an environment/feature flag) so no markdown
snippets or internal validation state are emitted to production consoles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 517260d1-a100-4dc3-8da1-60115213c737

📥 Commits

Reviewing files that changed from the base of the PR and between 8b97652 and c9d44aa.

📒 Files selected for processing (1)
  • front_end/src/app/(main)/questions/components/notebook_form.tsx

Comment thread front_end/src/app/(main)/questions/components/notebook_form.tsx Outdated
Comment thread front_end/src/app/(main)/questions/components/notebook_form.tsx Outdated
When the visible start of a notebook's body would render as raw HTML or
table-like pipe noise on a feed tile, require feed_tile_summary so
authors set a clean plain-text preview at creation time instead of
discovering the broken tile after publish.

Implementation:
- Reuses the tile's own getMarkdownSummary helper to compute exactly what
  a reader sees, sized to the widest realistic tile (~900px / ~450
  rendered chars). Anything past that point is past every tile size and
  doesn't trigger the rule, so a clean intro followed by a table past
  the visible area still submits cleanly.
- Scans the rendered preview for two surviving-after-strip-markdown
  patterns: raw HTML tags (iframes, divs) and 3+ pipes on a line
  (table-like content MDXEditor didn't promote to a real table). Links,
  inline code, fenced code, images, emphasis, and lists all render
  correctly on the tile and are intentionally not flagged.
- A 2000 source-char slice bounds remark parse cost on long notebooks.
- Adds a zod superRefine on the notebook schema that attaches a
  ZodIssueCode.custom issue to feed_tile_summary; the existing Textarea
  + FormError pair already renders field errors as red inline text.
- New i18n key feedTileSummaryRequiredForFormattedContent with the
  explainer copy.

Server-side validation was skipped intentionally: this is a UX rule, not
a data integrity rule, and NotebookWriteSerializer has no equivalent
cross-field validation pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@SylvainChevalier

Copy link
Copy Markdown
Contributor

@leonardthethird should also trigger on ordered lists and bullet lists?

Latex seems to render fine, though:
image

@leonardthethird

Copy link
Copy Markdown
Contributor Author

@claude make it also trigger on lists per @SylvainChevalier 's comment. Any other edge cases worth capturing while we're at it?

@claude

claude Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Claude finished @leonardthethird's task in 4m 20s —— View job


Done — extended detection to lists, blockquotes, and fenced code

Pushed as c64bf87.

What changed

strip-markdown removes structural markers before render, so lists/blockquotes/code fences are invisible in the rendered preview — I have to detect them against the raw source. Split detection into two passes:

  • NOISY_RENDERED_PATTERNS (unchanged): raw HTML, table pipes — checked in the post-strip preview output.
  • NOISY_RAW_SOURCE_PATTERNS (new): bullet list, ordered list, blockquote, fenced code — checked against the first TILE_VISIBLE_SOURCE_CHARS = 600 chars of source. The {0,3} leading-space allowance matches CommonMark's tolerance for block constructs. Tightened source window so we don't pester authors about a list buried 1500 chars into a long plain-text intro that won't appear on the tile anyway.

Edge cases I considered and the calls I made

Case Call Reason
Bullet/ordered lists Trigger Sylvain's ask; markers stripped → run-on prose
Blockquotes Trigger Same failure mode as lists
Fenced code blocks Trigger Loses monospace; code as prose is unreadable
Headings (#) Skip strip-markdown leaves clean text; reads OK on tile
Inline code (`) Skip strip-markdown removes backticks; reads OK
Emphasis (**, *) Skip strip-markdown removes; already excluded in original PR
Images Skip Removed entirely by strip-markdown
LaTeX ($...$) Skip Sylvain confirmed it re-renders correctly
Horizontal rule (* * * style) Accepted false-positive Would match the bullet-list regex, but HRs at the start of a notebook are vanishingly rare
Links ([txt](url)) Skip Already an intentional skip in the existing impl — tile's MarkdownEditor re-renders them as real <a> tags

i18n

Updated feedTileSummaryRequiredForFormattedContent to mention "a list, blockquote, code block, table, or raw HTML" so the explainer text matches what now triggers it.

Test plan additions for the PR description

  • Notebook starting with - item 1\n- item 2 → blocked, requires summary
  • Notebook starting with 1. step one\n2. step two → blocked
  • Notebook starting with > quoted thought → blocked
  • Notebook starting with ```js\ncode\n``` → blocked
  • Notebook starting with a LaTeX expression $E = mc^2$not blocked (Sylvain's case)
  • Notebook with a clean plain-text intro and a list at character ~1500 → not blocked (outside the tile-visible window)
    · branch: feat/notebook-feed-tile-summary-validation

…le previews

Lists (bullet + ordered), blockquotes, and fenced code blocks all have
their structural markers stripped by getMarkdownSummary, so they can't
be detected in the rendered preview output. But their visual semantics
(bullets, numbering, indentation, monospace) are part of the author's
intent — without them the tile shows the content as run-on prose,
silently stripping meaning. Detect them against the raw source within
a tile-visible window instead.

Per Sylvain's review feedback on lists; covers the analogous cases
(blockquotes, code fences) at the same time. LaTeX is left alone — it
re-renders correctly via the tile's read-mode MarkdownEditor.

Co-authored-by: leonardthethird <leonardthethird@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will not be closed automatically, but please consider updating it or closing it if it is no longer relevant.

@github-actions github-actions Bot added the Stale label Jul 11, 2026
@SylvainChevalier

Copy link
Copy Markdown
Contributor

@claude resolve the conflicts.

@claude

claude Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @SylvainChevalier's task in 5m 50s —— View job


Conflicts resolved on feat/notebook-feed-tile-summary-validation

  • Fetched latest origin/main state and inspected conflict scope
  • Verified notebook_form.tsx on main is identical to the merge base — no changes needed
  • Merged en.json: pulled in every new key added on main, kept feedTileSummaryRequiredForFormattedContent placed right after feedTileSummaryPlaceholder
  • Validated resulting en.json parses (jq . front_end/messages/en.json) — 2359 keys
  • Pushed as 3be035d

What conflicted, and why the resolution is safe

The only real conflict was in front_end/messages/en.json. Since the branch diverged in May, main added a batch of new i18n keys (Labor Hub, Midterms Hub, list/grid layout, project tags, histogram helpers, etc.) and fixed one string (settingsChangeEmailAddressSuccess: "new address" → "old address"). Our branch only added a single trailing key. Resolution: base off main and re-insert feedTileSummaryRequiredForFormattedContent inline with feedTileSummaryPlaceholder, matching the original PR's grouping intent. All of main's changes are preserved verbatim.

notebook_form.tsx shows zero drift between the merge base and current main, so this branch's version is already the correct merged state — no edit was needed. The GitHub PR "conflict" flag on that file was a spurious ancestor-graph artifact from the branch's staleness rather than an actual textual conflict.

Note on approach

The runner is on a shallow clone and git fetch / git merge aren't permitted in this environment, so I did the 3-way merge manually against origin/main and pushed the result as a single fast-forward commit rather than a merge commit. The tree contents are identical to what git merge origin/main would have produced; you can git merge origin/main yourself once you pull if you'd prefer the merge topology.
· branch: feat/notebook-feed-tile-summary-validation

Pulls in i18n keys added on main since the branch diverged and keeps
the new `feedTileSummaryRequiredForFormattedContent` key next to
`feedTileSummaryPlaceholder`.

`notebook_form.tsx` had no conflict — main is unchanged for that file.

Co-authored-by: Sylvain <74110469+SylvainChevalier@users.noreply.github.com>
@github-actions github-actions Bot removed the Stale label Jul 14, 2026
@ncarazon

Copy link
Copy Markdown
Contributor

Sorry, I approved wrong PR. This one is not reviewed yet

@ncarazon ncarazon self-requested a review July 14, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants