Skip to content

feat(shipguard): add persona-aware visual reports#51

Open
bacoco wants to merge 1 commit intomainfrom
codex/persona-aware-reports
Open

feat(shipguard): add persona-aware visual reports#51
bacoco wants to merge 1 commit intomainfrom
codex/persona-aware-reports

Conversation

@bacoco
Copy link
Copy Markdown
Owner

@bacoco bacoco commented May 5, 2026

What

Why

Testing

  • Tested locally with agent-browser
  • Screenshots verified
  • YAML manifests are valid

ShipGuard Change Report

  • UI-visible changes include a committed visual-tests/_results/change-reports/<report-id>/ source report, or this PR is not UI-visible.
  • Generated visual-tests/_results/persona-reports/<report-id>/ HTML is committed when the PR needs before/after review.
  • visual-tests/_results/review.html and visual-tests/_results/.server.pid are not committed.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

Warning

Rate limit exceeded

@bacoco has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 41 minutes and 25 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 801dfb7b-cf4f-4aa5-93fa-b1add0d44dd6

📥 Commits

Reviewing files that changed from the base of the PR and between 0fab173 and 8a66b1e.

📒 Files selected for processing (4)
  • plugins/shipguard/README.md
  • plugins/shipguard/skills/sg-visual-review/SKILL.md
  • plugins/shipguard/skills/sg-visual-review/build-review.mjs
  • plugins/shipguard/skills/sg-visual-review/examples/change-report.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/persona-aware-reports

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 and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces persona-aware client validation reports, enabling the generation of tailored HTML reports for different stakeholders like clients, product managers, and engineers. The changes include comprehensive documentation, an example report specification, and new logic in build-review.mjs for report normalization and rendering. The review feedback identifies several opportunities to improve the robustness of the build script, specifically by handling invalid date strings and malformed JSON files to prevent crashes, and ensuring asset URLs are properly HTML-escaped to mitigate potential injection vulnerabilities.

subtitle: raw.subtitle || raw.summary || '',
summary: raw.summary || raw.subtitle || '',
route: raw.route || raw.url || '',
generatedAt: raw.generated_at || raw.generatedAt || new Date().toISOString(),
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.

medium

If raw.generated_at or raw.generatedAt contains an invalid date string, new Date(report.generatedAt).toISOString() will throw a RangeError later in the rendering process (line 562), crashing the build script. It is safer to validate the date string during normalization and fall back to the current date if it's invalid.

    generatedAt: (function(d) { return isNaN(new Date(d).getTime()) ? new Date().toISOString() : d; })(raw.generated_at || raw.generatedAt || new Date().toISOString()),

Comment on lines +461 to +466
for (const entry of readdirSync(CHANGE_REPORTS_DIR, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const reportPath = join(CHANGE_REPORTS_DIR, entry.name, 'report.json');
if (!existsSync(reportPath)) continue;
reports.push(normalizeChangeReport(entry.name, readJson(reportPath)));
}
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.

medium

The build script will crash if any report.json file is malformed or fails validation in normalizeChangeReport. Wrapping this call in a try-catch block and logging a warning allows the build to continue for other valid reports, providing a more robust user experience.

  for (const entry of readdirSync(CHANGE_REPORTS_DIR, { withFileTypes: true })) {
    if (!entry.isDirectory()) continue;
    const reportPath = join(CHANGE_REPORTS_DIR, entry.name, 'report.json');
    if (!existsSync(reportPath)) continue;
    try {
      reports.push(normalizeChangeReport(entry.name, readJson(reportPath)));
    } catch (e) {
      console.warn(`  WARN: Failed to process change report ${entry.name}: ${e.message}`);
    }
  }

Comment on lines +491 to +493
<a class="shot" href="${href}">
<div class="shot-label"><strong>${escapeHtml(label)}</strong><span>${escapeHtml(shot.caption || '')}</span></div>
<img src="${href}" alt="${escapeHtml(shot.alt || shot.caption || label)}" loading="lazy" />
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.

medium

The href generated by reportAssetHref should be HTML-escaped before being used in attributes. While reportAssetHref handles URL encoding for local paths, full URLs provided in the report JSON might contain characters like & or " that could break the HTML structure or lead to attribute injection.

    <a class="shot" href="${escapeHtml(href)}">
      <div class="shot-label"><strong>${escapeHtml(label)}</strong><span>${escapeHtml(shot.caption || '')}</span></div>
      <img src="${escapeHtml(href)}" alt="${escapeHtml(shot.alt || shot.caption || label)}" loading="lazy" />

Copy link
Copy Markdown

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

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: 8a66b1efdb

ℹ️ 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".

const audiences = asArray(configuredAudiences).map(normalizeAudience).filter(Boolean);
if (audiences.length === 0) throw new Error(`Invalid report.json for ${id}: at least one audience is required`);
return {
id: slugify(raw.id || id),
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 Keep report IDs tied to their source folder

Do not derive report.id from arbitrary JSON when assets are still read from change-reports/<folder>/.... If report.json sets an id different from its directory name, generated image links resolve to change-reports/<json-id>/... and 404, so before/after evidence disappears in the persona pages. This also allows two different folders with the same JSON id to collide in one output directory.

Useful? React with 👍 / 👎.

<div class="metric"><strong>${report.changes.length}</strong><span>changes to review</span></div>
<div class="metric"><strong>${escapeHtml(report.status)}</strong><span>report status</span></div>
<div class="metric"><strong>${escapeHtml(report.route || 'n/a')}</strong><span>route / flow</span></div>
<div class="metric"><strong>${escapeHtml(new Date(report.generatedAt).toISOString().slice(0, 10))}</strong><span>generated</span></div>
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 Guard invalid generated_at values before formatting

Avoid calling toISOString() on unvalidated report.generatedAt. A malformed generated_at value in report.json produces RangeError: Invalid time value, which aborts report generation instead of falling back to a safe date display; one bad report can therefore break persona report output entirely.

Useful? React with 👍 / 👎.

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.

1 participant