From f2ef08cbd0fa9131020d8f46bd407227b09313f9 Mon Sep 17 00:00:00 2001 From: Chumaniac <121109854+Chumaniac@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:31:29 +0800 Subject: [PATCH 1/2] feat(reports): open offline findings by review priority --- CHANGELOG.md | 7 +++++++ src/reporters/verification-html.ts | 21 ++++++++++++++------- tests/reporters/verification-html.test.ts | 23 +++++++++++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 700263c..ca4b71b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Unreleased source - 2026-10-21 + +- Open offline verification reports at the first priority needing review and + jump directly to other severity groups, including without JavaScript. +- Keep complete counts and the existing detail budget; omit empty groups and + use readable status labels. Redaction and verification outcomes are unchanged. + ## 0.3.0 - 2026-10-20 - Review all validated cross-file declarations and inspection limits in offline HTML, text and JSON reports, including CSV summaries, reference metadata and keyed integer allocations. diff --git a/src/reporters/verification-html.ts b/src/reporters/verification-html.ts index 95338e3..009c186 100644 --- a/src/reporters/verification-html.ts +++ b/src/reporters/verification-html.ts @@ -15,12 +15,19 @@ export function renderVerificationHtml(input: VerificationReport): string { const report = redactLocalPaths(input); const issues = report.issues?.length ? report.issues : toIssues(report.findings); const counts = report.summary; + const priorities = [ + { severity: "critical", label: "Critical issues", help: "Resolve these issues before reusing this Skill." }, + { severity: "error", label: "Errors", help: "Fix the failed checks, then verify again." }, + { severity: "warn", label: "Warnings", help: "Review these warnings against your intended use." }, + { severity: "info", label: "Information", help: "Review the recorded checks and notes." }, + ].map(priority => ({ ...priority, issues: issues.filter(issue => issue.finding.severity === priority.severity) })) + .filter(priority => priority.issues.length > 0); + const links = priorities.length ? `` : ""; let remaining = DETAIL_LIMIT; - const groups = (["critical", "error", "warn", "info"] as const).map(severity => { - const all = issues.filter(issue => issue.finding.severity === severity); + const groups = priorities.map(({ severity, label, help, issues: all }, groupIndex) => { const shown = all.slice(0, remaining); remaining -= shown.length; - return `
${severity.toUpperCase()} · ${all.length} issues${shown.map((issue, index) => { + return `
${label} · ${all.length} issues

${help}

${shown.map((issue, index) => { const f = issue.finding; const resolution = issue.resolutions[0]; const steps = resolution?.kind === "manual" ? resolution.steps.slice(0, 8) : []; @@ -29,14 +36,14 @@ export function renderVerificationHtml(input: VerificationReport): string {

${text(issue.explanation.cause)}

Next step: ${text(resolution?.title ?? f.remediation ?? issue.explanation.impact)}

${steps.length ? `
    ${steps.map(step => `
  1. ${text(step)}
  2. `).join("")}
` : ""}
Issue reference${text(issue.id)}

${text(issue.location?.path ?? "Location not included")}

`; - }).join("")}${all.length > shown.length ? `

Showing ${shown.length} of ${all.length} issues in this group. Use JSON for complete evidence.

` : ""}${all.length ? "" : "

No issues in this group.

"}
`; + }).join("")}${all.length > shown.length ? `

Showing ${shown.length} of ${all.length} issues in this group. Use JSON for complete evidence.

` : ""}
`; }).join(""); const title = report.exitCode === 2 ? "Verification setup required" : counts.fail ? "Changes require attention" : counts.unknown || counts.warn ? "Review incomplete evidence" : "Declared checks passed"; return ` SkillSync · ${title} - +
SkillSync · Offline verification review
-

${title}

Read the cause and next step for each issue. These deterministic checks do not establish runtime safety, authenticated provenance, or approval.

${(["pass", "fail", "warn", "unknown"] as const).map(key => `
${text(counts[key])}${key}
`).join("")}

${text(counts.total)} findings · ${text(issues.length)} deduplicated issues · exit code ${report.exitCode}

-

Issues and next steps

Groups are ordered by severity. At most ${DETAIL_LIMIT} issues are shown. Expand information groups to review successful checks.

${groups}
+

${title}

These checks read declared metadata. They do not establish runtime safety, authenticated provenance, or approval.

${([{ key: "pass", label: "Passed" }, { key: "fail", label: "Failed" }, { key: "warn", label: "Warnings" }, { key: "unknown", label: "Unverified" }] as const).map(({ key, label }) => `
${text(counts[key])}${label}
`).join("")}

${text(counts.total)} findings · ${text(issues.length)} deduplicated issues · exit code ${report.exitCode}

${links}
+

Issues and next steps

${priorities.length ? `

The first group is open. Choose a priority above to open another group. At most ${DETAIL_LIMIT} issue details are included; use JSON for the full report.

${groups}` : "

No issues were reported. Review the scope and selected targets below.

"}

Scope and evidence

${report.targets.length} selected targets. ${text(report.generated_at)}

Local paths and raw evidence contents are excluded. No Skill was executed and no fix was applied by this report.

\n`; } diff --git a/tests/reporters/verification-html.test.ts b/tests/reporters/verification-html.test.ts index d25d6b0..0d2ad08 100644 --- a/tests/reporters/verification-html.test.ts +++ b/tests/reporters/verification-html.test.ts @@ -16,3 +16,26 @@ it("escapes metadata, hides raw evidence, and caps details while retaining total expect(html).not.toContain("/private/work"); expect(html).toContain("<local-path>"); }); + +it("links directly to severity groups and opens the first group needing review", () => { + const findings = (["warn", "critical", "error", "info"] as const).map(severity => ({ level: 1 as const, severity, status: "fail" as const, code: `test-${severity}`, skill: "fixture", message: "Review this check", remediation: "Check the saved input", evidence: [] })); + const report: VerificationReport = { schema_version: 1, generated_at: "now", targets: [], findings, issues: toIssues(findings), summary: { total: 4, pass: 0, fail: 4, warn: 0, unknown: 0 }, exitCode: 1 }; + const html = renderVerificationHtml(report); + expect(html).toContain('aria-label="Issue priorities"'); + expect(html).toContain('href="#issues-critical-start"'); + expect(html).toContain('id="issues-critical" open'); + expect(html).not.toContain('id="issues-error" open'); + for (const severity of ["critical", "error", "warn", "info"]) { + expect(html).toContain(`id="issues-${severity}-start"`); + } + expect(html).toContain("Unverified"); + expect(html).not.toContain("CRITICAL ·"); +}); + +it("does not fill an empty report with four empty severity panels", () => { + const report: VerificationReport = { schema_version: 1, generated_at: "now", targets: [], findings: [], issues: [], summary: { total: 0, pass: 0, fail: 0, warn: 0, unknown: 0 }, exitCode: 0 }; + const html = renderVerificationHtml(report); + expect(html).toContain("No issues were reported."); + expect(html).not.toContain('class="issue-group"'); + expect(html).not.toContain('aria-label="Issue priorities"'); +}); From 1e6e4749c6340e1041cf8d5062294424c0e19465 Mon Sep 17 00:00:00 2001 From: Chumaniac <121109854+Chumaniac@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:36:35 +0800 Subject: [PATCH 2/2] fix(reports): use singular labels for one review item --- src/reporters/verification-html.ts | 4 ++-- tests/reporters/verification-html.test.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/reporters/verification-html.ts b/src/reporters/verification-html.ts index 009c186..726043d 100644 --- a/src/reporters/verification-html.ts +++ b/src/reporters/verification-html.ts @@ -27,7 +27,7 @@ export function renderVerificationHtml(input: VerificationReport): string { const groups = priorities.map(({ severity, label, help, issues: all }, groupIndex) => { const shown = all.slice(0, remaining); remaining -= shown.length; - return `
${label} · ${all.length} issues

${help}

${shown.map((issue, index) => { + return `
${label} · ${all.length} ${all.length === 1 ? "issue" : "issues"}

${help}

${shown.map((issue, index) => { const f = issue.finding; const resolution = issue.resolutions[0]; const steps = resolution?.kind === "manual" ? resolution.steps.slice(0, 8) : []; @@ -45,5 +45,5 @@ export function renderVerificationHtml(input: VerificationReport): string {
SkillSync · Offline verification review

${title}

These checks read declared metadata. They do not establish runtime safety, authenticated provenance, or approval.

${([{ key: "pass", label: "Passed" }, { key: "fail", label: "Failed" }, { key: "warn", label: "Warnings" }, { key: "unknown", label: "Unverified" }] as const).map(({ key, label }) => `
${text(counts[key])}${label}
`).join("")}

${text(counts.total)} findings · ${text(issues.length)} deduplicated issues · exit code ${report.exitCode}

${links}

Issues and next steps

${priorities.length ? `

The first group is open. Choose a priority above to open another group. At most ${DETAIL_LIMIT} issue details are included; use JSON for the full report.

${groups}` : "

No issues were reported. Review the scope and selected targets below.

"}
-

Scope and evidence

${report.targets.length} selected targets. ${text(report.generated_at)}

Local paths and raw evidence contents are excluded. No Skill was executed and no fix was applied by this report.

Generated from local verification metadata. No scripts, external assets, or network requests. Keep the JSON report for complete machine-readable evidence.
\n`; +

Scope and evidence

${report.targets.length} selected ${report.targets.length === 1 ? "target" : "targets"}. ${text(report.generated_at)}

Local paths and raw evidence contents are excluded. No Skill was executed and no fix was applied by this report.

\n`; } diff --git a/tests/reporters/verification-html.test.ts b/tests/reporters/verification-html.test.ts index 0d2ad08..9e7782a 100644 --- a/tests/reporters/verification-html.test.ts +++ b/tests/reporters/verification-html.test.ts @@ -30,6 +30,7 @@ it("links directly to severity groups and opens the first group needing review", } expect(html).toContain("Unverified"); expect(html).not.toContain("CRITICAL ·"); + expect(html).not.toContain("1 issues"); }); it("does not fill an empty report with four empty severity panels", () => {