20d13fc678
- Remove unused StripOverlayLayer import and stripOverlayLayer variable from module.js - Add comprehensive JSDoc annotations to FoundryAdapter.js methods (settings, socket, users, scenes, notifications, hooks) - Add /* global Dialog */ comment to PlayerPrivacyPanel.js for ESLint - Remove unused _force parameter from GMPlayerPrivacySelector.js render() method - Fix PlayerPrivacyPanelMenu.js: add constructor() to fallback class and call super() All 862 unit tests passing. All Story 4.2 acceptance criteria met. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
326 lines
11 KiB
HTML
326 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<!--
|
|
PRD Validation Report — skeleton template.
|
|
|
|
This file is a starter the synthesis pass fills in directly. There is no
|
|
substitution engine. The LLM:
|
|
1. Reads {doc_workspace}/review-rubric.md and every review-{slug}.md from
|
|
additional reviewers.
|
|
2. Copies this skeleton.
|
|
3. Replaces the placeholder content (everything between TEMPLATE markers)
|
|
with the consolidated review, preserving the structure and CSS.
|
|
4. Writes the result to {doc_workspace}/validation-report.html.
|
|
5. Writes a markdown twin to {doc_workspace}/validation-report.md.
|
|
|
|
Visual rules the LLM must preserve:
|
|
- The container width, the color tokens, the typography.
|
|
- One dimension = one collapsible <section class="dimension">.
|
|
- Verdict pill uses the verdict-* class matching its judgment.
|
|
- Severity badge uses the sev-* class matching its level.
|
|
- Each extra reviewer (adversarial, etc.) gets its own collapsible section
|
|
below the rubric dimensions.
|
|
- The footer always shows the artifact paths and timestamp.
|
|
-->
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>PRD Validation: TEMPLATE_PRD_NAME</title>
|
|
<style>
|
|
:root {
|
|
--bg: #fafaf9;
|
|
--surface: #ffffff;
|
|
--border: #e7e5e4;
|
|
--text: #1c1917;
|
|
--muted: #78716c;
|
|
|
|
--verdict-strong: #16a34a;
|
|
--verdict-adequate: #65a30d;
|
|
--verdict-thin: #d97706;
|
|
--verdict-broken: #dc2626;
|
|
|
|
--sev-low: #64748b;
|
|
--sev-medium: #ca8a04;
|
|
--sev-high: #ea580c;
|
|
--sev-critical: #dc2626;
|
|
|
|
--grade-exc: #16a34a;
|
|
--grade-good: #65a30d;
|
|
--grade-fair: #d97706;
|
|
--grade-poor: #dc2626;
|
|
}
|
|
* { box-sizing: border-box; }
|
|
html, body { margin: 0; padding: 0; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
font-size: 15px;
|
|
}
|
|
.container { max-width: 960px; margin: 0 auto; padding: 32px 24px 64px; }
|
|
|
|
header.report-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 24px;
|
|
padding-bottom: 16px;
|
|
border-bottom: 1px solid var(--border);
|
|
margin-bottom: 24px;
|
|
}
|
|
.title h1 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
|
|
.title .subtitle { color: var(--muted); font-size: 13px; margin-top: 4px; font-family: ui-monospace, "SF Mono", Menlo, monospace; }
|
|
.grade {
|
|
padding: 10px 18px;
|
|
border-radius: 8px;
|
|
font-weight: 600;
|
|
color: white;
|
|
font-size: 15px;
|
|
white-space: nowrap;
|
|
}
|
|
.grade-excellent { background: var(--grade-exc); }
|
|
.grade-good { background: var(--grade-good); }
|
|
.grade-fair { background: var(--grade-fair); }
|
|
.grade-poor { background: var(--grade-poor); }
|
|
|
|
.synthesis {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-left: 3px solid var(--muted);
|
|
border-radius: 8px;
|
|
padding: 18px 22px;
|
|
margin-bottom: 24px;
|
|
font-size: 15.5px;
|
|
}
|
|
.synthesis p { margin: 0 0 10px; }
|
|
.synthesis p:last-child { margin-bottom: 0; }
|
|
|
|
.dimension-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
gap: 10px;
|
|
margin-bottom: 24px;
|
|
}
|
|
.dim-card {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 12px 14px;
|
|
}
|
|
.dim-card .dim-name { font-size: 13px; color: var(--muted); margin-bottom: 6px; }
|
|
.dim-card .dim-verdict { font-size: 14px; font-weight: 600; }
|
|
|
|
section.dimension, section.reviewer-section { margin-bottom: 14px; }
|
|
section.dimension details, section.reviewer-section details {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
section summary {
|
|
padding: 14px 20px;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
list-style: none;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
section summary::-webkit-details-marker { display: none; }
|
|
section summary::before {
|
|
content: "▸";
|
|
display: inline-block;
|
|
color: var(--muted);
|
|
transition: transform 0.15s ease;
|
|
}
|
|
section details[open] summary::before { transform: rotate(90deg); }
|
|
section summary h2 {
|
|
display: inline;
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
letter-spacing: -0.005em;
|
|
flex: 1;
|
|
}
|
|
.verdict-pill {
|
|
font-size: 11px;
|
|
padding: 3px 10px;
|
|
border-radius: 999px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
color: white;
|
|
}
|
|
.verdict-strong { background: var(--verdict-strong); }
|
|
.verdict-adequate { background: var(--verdict-adequate); }
|
|
.verdict-thin { background: var(--verdict-thin); }
|
|
.verdict-broken { background: var(--verdict-broken); }
|
|
|
|
.dim-body { padding: 4px 20px 18px; }
|
|
.dim-judgment { color: var(--text); font-size: 14.5px; }
|
|
.dim-judgment p { margin: 0 0 10px; }
|
|
|
|
.findings-list { padding: 0 20px 4px; }
|
|
article.finding {
|
|
padding: 14px 0;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
article.finding:first-child { border-top: none; }
|
|
article.finding header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
margin-bottom: 6px;
|
|
}
|
|
.badge {
|
|
font-size: 10.5px;
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
line-height: 1.4;
|
|
}
|
|
.badge-sev-low { background: rgba(100, 116, 139, 0.12); color: var(--sev-low); }
|
|
.badge-sev-medium { background: rgba(202, 138, 4, 0.14); color: var(--sev-medium); }
|
|
.badge-sev-high { background: rgba(234, 88, 12, 0.14); color: var(--sev-high); }
|
|
.badge-sev-critical { background: rgba(220, 38, 38, 0.14); color: var(--sev-critical); }
|
|
.finding-title { margin: 0; font-size: 15px; font-weight: 500; flex: 1; min-width: 200px; }
|
|
.finding-location { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; color: var(--muted); }
|
|
.finding-note, .finding-fix { margin-top: 6px; font-size: 14px; }
|
|
.finding-fix strong { color: var(--muted); font-weight: 500; }
|
|
|
|
.reviewer-source {
|
|
font-size: 12px;
|
|
color: var(--muted);
|
|
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
}
|
|
|
|
.mechanical {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 16px 20px;
|
|
margin-top: 24px;
|
|
font-size: 14px;
|
|
}
|
|
.mechanical h3 { margin: 0 0 10px; font-size: 14px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
|
|
.mechanical ul { margin: 0; padding-left: 20px; }
|
|
.mechanical li { margin-bottom: 4px; color: var(--text); }
|
|
|
|
footer.report-footer {
|
|
margin-top: 40px;
|
|
padding-top: 16px;
|
|
border-top: 1px solid var(--border);
|
|
font-size: 12px;
|
|
color: var(--muted);
|
|
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
}
|
|
footer .meta { display: flex; gap: 24px; flex-wrap: wrap; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
|
|
<!-- TEMPLATE: header. Fill prd name, prd path, grade text & class. -->
|
|
<header class="report-header">
|
|
<div class="title">
|
|
<h1>TEMPLATE_PRD_NAME — Validation Report</h1>
|
|
<div class="subtitle">TEMPLATE_PRD_PATH</div>
|
|
</div>
|
|
<div class="grade TEMPLATE_GRADE_CLASS">TEMPLATE_GRADE</div>
|
|
</header>
|
|
|
|
<!-- TEMPLATE: overall synthesis paragraphs. Lift directly from
|
|
review-rubric.md "Overall verdict" section; expand if extra reviewers
|
|
materially shift the picture. Wrap each paragraph in <p>. -->
|
|
<div class="synthesis">
|
|
<p>TEMPLATE_SYNTHESIS_PARAGRAPH</p>
|
|
</div>
|
|
|
|
<!-- TEMPLATE: dimension summary cards. One per rubric dimension. The
|
|
dim-verdict text uses one of: strong | adequate | thin | broken. -->
|
|
<div class="dimension-summary">
|
|
<div class="dim-card">
|
|
<div class="dim-name">Decision-readiness</div>
|
|
<div class="dim-verdict" style="color: var(--verdict-TEMPLATE_VERDICT)">TEMPLATE_VERDICT_TEXT</div>
|
|
</div>
|
|
<!-- repeat for each of the seven dimensions -->
|
|
</div>
|
|
|
|
<!-- TEMPLATE: one section per rubric dimension. Skip a dimension entirely
|
|
if the rubric review marked it n/a for this PRD (e.g. downstream
|
|
usability for a standalone PRD). Open the section by default if
|
|
verdict is thin or broken. -->
|
|
<section class="dimension">
|
|
<details open>
|
|
<summary>
|
|
<h2>Decision-readiness</h2>
|
|
<span class="verdict-pill verdict-TEMPLATE_VERDICT">TEMPLATE_VERDICT_TEXT</span>
|
|
</summary>
|
|
<div class="dim-body">
|
|
<div class="dim-judgment">
|
|
<p>TEMPLATE_DIMENSION_JUDGMENT</p>
|
|
</div>
|
|
</div>
|
|
<div class="findings-list">
|
|
<!-- TEMPLATE: zero or more findings -->
|
|
<article class="finding">
|
|
<header>
|
|
<span class="badge badge-sev-TEMPLATE_SEVERITY">TEMPLATE_SEVERITY</span>
|
|
<h3 class="finding-title">TEMPLATE_FINDING_TITLE</h3>
|
|
<span class="finding-location">TEMPLATE_LOCATION</span>
|
|
</header>
|
|
<div class="finding-note">TEMPLATE_FINDING_NOTE</div>
|
|
<div class="finding-fix"><strong>Fix:</strong> TEMPLATE_SUGGESTED_FIX</div>
|
|
</article>
|
|
</div>
|
|
</details>
|
|
</section>
|
|
|
|
<!-- TEMPLATE: one section per extra reviewer that ran (adversarial, etc.).
|
|
Skip this block entirely if only the rubric walker ran. -->
|
|
<section class="reviewer-section">
|
|
<details>
|
|
<summary>
|
|
<h2>Adversarial review</h2>
|
|
<span class="reviewer-source">TEMPLATE_REVIEWER_SOURCE_FILE</span>
|
|
</summary>
|
|
<div class="dim-body">
|
|
<div class="dim-judgment">
|
|
<p>TEMPLATE_REVIEWER_PREAMBLE</p>
|
|
</div>
|
|
</div>
|
|
<div class="findings-list">
|
|
<article class="finding">
|
|
<header>
|
|
<span class="badge badge-sev-TEMPLATE_SEVERITY">TEMPLATE_SEVERITY</span>
|
|
<h3 class="finding-title">TEMPLATE_FINDING_TITLE</h3>
|
|
<span class="finding-location">TEMPLATE_LOCATION</span>
|
|
</header>
|
|
<div class="finding-note">TEMPLATE_FINDING_NOTE</div>
|
|
<div class="finding-fix"><strong>Fix:</strong> TEMPLATE_SUGGESTED_FIX</div>
|
|
</article>
|
|
</div>
|
|
</details>
|
|
</section>
|
|
|
|
<!-- TEMPLATE: mechanical notes — short, bulleted. Skip if there are none. -->
|
|
<div class="mechanical">
|
|
<h3>Mechanical notes</h3>
|
|
<ul>
|
|
<li>TEMPLATE_MECHANICAL_NOTE</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<footer class="report-footer">
|
|
<div class="meta">
|
|
<span>Rubric: TEMPLATE_RUBRIC_PATH</span>
|
|
<span>Generated: TEMPLATE_TIMESTAMP</span>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html>
|