Identify Content comments posted via AI - #4513
Conversation
This comment has been minimized.
This comment has been minimized.
Visual recap — generation failedThe visual recap could not be generated for this pull request. This is informational only and does not block the PR. Diagnostic: No plan URL: create-visual-recap failed 403 Forbidden: {"error":"Creating an org-visible visual recap requires an active organization. Connect Plan from an organization or publish with private visibility."} Agent output: The supplied diff is actually 1,007 lines on disk (rather than the stated 610), so I completed the sequential pass through line 1,007 before authoring. The recap will represent the comment-sidebar entry, attribution tooltip, resolved-state card, persistence model, notification flow, every changed file, and six load-bearing code/test excerpts. stderr: Reading additional input from stdin... 2026-09-08T23:23:30.119086Z ERROR codex_core::tools::router: error=apply_patch verification failed: invalid patch: multiple operations target /home/runner/work/agent-native/agent-native/recap-source.json |
There was a problem hiding this comment.
Builder reviewed your changes and found 1 potential issue 🟡
Review Details
Incremental Code Review Summary
The latest PR head fixes the previously reported HTML injection issue by escaping attribution and comment excerpts before rendering email HTML, and adds regression coverage using the real email renderer. I verified that fix and resolved the prior review thread. The additive comment attribution schema, action caller mapping, per-comment UI badge behavior, localization, and updated sidebar layout remain consistent.
New Finding
Medium: The new escaping is applied to strings passed to renderEmail, which uses the same paragraph values for both HTML and plain-text output. Because the text path strips tags but does not decode HTML entities, recipients reading the plain-text email now see encoded content such as <script> and & instead of the original text. The implementation should provide separate HTML-safe and plain-text values, or update the renderer contract to decode entities for the text output.
Browser Testing
FULL browser verification was attempted because no previous run had produced verified results. The dev server and seeded routes were healthy, but all 17 planned UI cases were couldnt_verify because Chrome automation tools were unavailable in executor sessions.
Comments submitted through MCP or the in-app agent currently look like comments the account holder posted personally. This change keeps the authenticated account as the author and adds a separate AI badge, with “Posted via AI on behalf of …” and the submission source available from the badge.
Content records the server-provided action caller when creating each comment or reply. The two new database columns are nullable and additive: historical comments remain unclassified, and edits or thread resolution preserve the original submission attribution. The same account-based access checks still apply. Notification emails carry the AI attribution too, and the UI strings are localized. Attribution and comment excerpts are HTML-escaped before entering email paragraph markup; regression tests use the real email renderer for MCP, in-app agent, and human submissions.
This describes how a comment was submitted, not whether AI wrote every word. MCP and native in-app action tools are covered. Local development agents that invoke actions through a shell still arrive as ordinary CLI calls; those remain unbadged because that path does not carry reliable agent provenance. Client/model names are not inferred.
Validation: 85 focused tests pass, including a real database create/read/edit/resolve round trip; direct Content TypeScript compilation and localization checks pass. A real local MCP call persisted the expected source and account. All 71 repository guards pass. Independent technical review has no remaining findings after the touch, card activation, and keyboard focus repairs. Independent browser QA passes source disclosure on desktop hover/keyboard and mobile tap, unbadged human replies and reload persistence, and attribution retention through resolve/reopen. A history-boundary recheck at 390px, 768px, and 1280px confirms the repaired history boundary keeps badge disclosure inside its panel and Reopen visibly settles in under one second. The final visual polish was separately checked at 390px and 1280px: the badge is 16px tall within a larger invisible hit target, avatar/name/badge alignment and quote/body indentation are consistent, and hover, keyboard, Escape, and touch disclosure pass. The earlier technical review has no remaining findings; the final delta only changes presentation. The final email-escaping repair passes all 15 notification tests, direct TypeScript compilation, and independent security review. A direct real-renderer assertion also confirms escaped attribution and excerpts decode back to readable plain text while remaining escaped in HTML:
stripTags()inpackages/core/src/server/email-template.tsdecodes entities after stripping tags. The automated plain-text regression report does not reproduce. These are local development checks; no production acceptance is claimed.The repository's Content typecheck wrapper emits a production database configuration warning in this local-only environment; direct TypeScript compilation passes. No production deployment was attempted.