altimate-receipts 0.3.2 β 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -38
- package/dist/cli.js +519 -66
- package/dist/cli.js.map +1 -1
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -176,6 +176,21 @@ function changedFilesBlock(files) {
|
|
|
176
176
|
"</details>"
|
|
177
177
|
].join("\n");
|
|
178
178
|
}
|
|
179
|
+
function parseSemver(v) {
|
|
180
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(v ?? "").trim());
|
|
181
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
182
|
+
}
|
|
183
|
+
function semverLt(a, b) {
|
|
184
|
+
const pa = parseSemver(a);
|
|
185
|
+
const pb = parseSemver(b);
|
|
186
|
+
if (!pa || !pb) return false;
|
|
187
|
+
for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pa[i] < pb[i];
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
function upgradeHint(receiptVer, compareVer) {
|
|
191
|
+
if (!compareVer || !semverLt(receiptVer, compareVer)) return null;
|
|
192
|
+
return `<sub>\u{1F9FE} Receipt generated by \`receipts@${receiptVer}\` \xB7 this check runs \`@${compareVer}\` \u2014 \`npm i -g altimate-receipts@latest\` (or \`npx altimate-receipts@latest\`) for the newer checks.</sub>`;
|
|
193
|
+
}
|
|
179
194
|
function fullBreakdown(p, ev, cats, operatorCount, opts, feedback) {
|
|
180
195
|
const inner = [];
|
|
181
196
|
const files = p.scope?.kind === "diff" ? p.scope.files ?? [] : [];
|
|
@@ -195,6 +210,8 @@ function fullBreakdown(p, ev, cats, operatorCount, opts, feedback) {
|
|
|
195
210
|
inner.push(
|
|
196
211
|
`<sub>${signed}Re-derivable (L1: \`receipts verify <receipt> --transcript <t>\`) \xB7 deterministic \xB7 0 model calls \xB7 **evidence, not judgement** \xB7 [trust model](${TRUST_DOC_URL}).</sub>`
|
|
197
212
|
);
|
|
213
|
+
const hint = upgradeHint(p.generator?.version, opts.compareVersion);
|
|
214
|
+
if (hint) inner.push(hint);
|
|
198
215
|
inner.push(feedback);
|
|
199
216
|
return [
|
|
200
217
|
"<details><summary>Full breakdown \u2014 files, checks, cost, provenance</summary>",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/report/prComment.ts"],"sourcesContent":["import type { Severity } from \"../findings/findings.js\";\nimport { formatCostAlways, formatTokens } from \"../findings/format.js\";\nimport { type GradeLetter, gradeLetter } from \"../findings/grade.js\";\nimport { destructiveOutsideRepo, findingSurface } from \"../findings/surface.js\";\nimport type { Receipt, ReceiptFinding } from \"../receipt/build.js\";\nimport { type CategorizedChecks, categorize } from \"./checks.js\";\nimport { renderLedger } from \"./ledger.js\";\n\n// command classes ordered most-notable first, for the effort breakdown.\nconst CLASS_ORDER = [\n \"mutating\",\n \"vcs-history\",\n \"transport\",\n \"test\",\n \"build\",\n \"read-only\",\n \"opaque\",\n];\n\n/** Stable marker so the Action can find and update its own comment. */\nexport const PR_COMMENT_MARKER = \"<!-- verified-by-receipts -->\";\n\n// Canonical docs (absolute so the links work from any repo's PR comment) + the one-line\n// plain-English explainer for first-time viewers. Kept identical in action/verified-by.mjs.\nconst TRUST_DOC_URL = \"https://github.com/AltimateAI/altimate-receipts/blob/main/docs/trust.md\";\nconst ABOUT_DOC_URL = \"https://github.com/AltimateAI/altimate-receipts/blob/main/docs/problems.md\";\nconst EXPLAINER = `<sub>π€ A deterministic check that reads the coding agent's **own transcript** and reports what it changed, ran, and claimed on this PR β evidence, **not a code-quality verdict**. New here? [What the grade & checks mean βΊ](${ABOUT_DOC_URL})</sub>`;\n// Dogfooding feedback channel β a prefilled issue against the receipts repo so a false\n// positive (the thing that kills the near-zero-FP trust pitch) is one click to report and\n// collects centrally. The reaction ask uses GitHub's native comment reactions (queryable).\n// Kept identical in action/verified-by.mjs.\nconst FEEDBACK_ISSUE_BASE = \"https://github.com/AltimateAI/altimate-receipts/issues/new\";\n\n/**\n * Build the feedback footer with a **prefilled** false-positive issue link β title from\n * the top flagged finding, body pre-populated with the PR link (a `__PR_URL__` token the\n * Action substitutes, like `__ATTESTATION_URL__`), agent, grade, and the flagged\n * finding(s) by title+id β so the reporter only writes *why*. Identical in both renderers.\n */\nfunction feedbackLine(\n agent: string,\n grade: GradeLetter,\n flagged: readonly ReceiptFinding[],\n): string {\n const findingLines = flagged.length\n ? flagged.slice(0, 8).map((f) => `- ${f.title} (\\`${f.id}\\`)`)\n : [\"- (nothing was flagged β describe what you expected)\"];\n const body = [\n \"**Repo / PR:** __PR_URL__\",\n `**Agent:** ${agent} Β· **Grade:** ${grade}`,\n \"\",\n \"**Flagged finding(s) being disputed:**\",\n ...findingLines,\n \"\",\n \"**Why this is a false positive / noise:**\",\n \"_(your notes β what should it have done instead?)_\",\n ].join(\"\\n\");\n const title = flagged.length ? `False positive: ${flagged[0].title}` : \"Receipts feedback\";\n const qs = `labels=${encodeURIComponent(\"false-positive,dogfooding\")}&title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`;\n const url = `${FEEDBACK_ISSUE_BASE}?${qs}`;\n return `<sub>π¬ Wrong call or noise? **[Report it β prefilled βΊ](${url})** (only the \"why\" is left to write) Β· or use the π reaction button on this comment to vote π / π. It tunes the checks.</sub>`;\n}\n\n/** Traffic-light dot per grade β a glanceable status glyph in the headline. */\nconst GRADE_DOT: Record<GradeLetter, string> = { A: \"π’\", B: \"π‘\", C: \"π \", F: \"π΄\" };\n\n/** One-line verdict, scaled to the grade. */\nconst VERDICT: Record<GradeLetter, string> = {\n A: \"Safe to merge\",\n B: \"Minor things to check\",\n C: \"Needs a close review\",\n F: \"Do not merge without review\",\n};\n\n/** GitHub alert kind used to frame a flagged change β none on a clean run (no alarm). */\nconst CALLOUT: Record<GradeLetter, string | null> = {\n A: null,\n B: \"NOTE\",\n C: \"WARNING\",\n F: \"CAUTION\",\n};\n\nconst ICON: Record<Severity, string> = { critical: \"β\", high: \"β οΈ\", medium: \"π\", low: \"Β·\" };\nconst ORDER: Record<Severity, number> = { critical: 0, high: 1, medium: 2, low: 3 };\n\nexport interface PrCommentOptions {\n /** link to the signed attestation / Rekor entry, when available */\n attestationUrl?: string;\n /** whether the Receipt was Sigstore-signed in this run */\n signed?: boolean;\n}\n\n/**\n * Render the \"Verified-by: Receipts\" PR comment (Markdown). The Receipt is assumed\n * already redacted. Two zones (progressive disclosure): an always-visible verdict +\n * what-changed + the flagged findings, then the full 14-check catalog collapsed in a\n * `<details>` (open only when something fired). Effort/cost is demoted to a muted\n * footer β it's session-level, not this diff. Includes the trust caveat (SPEC-0005 R6).\n */\nexport function renderPrComment(receipt: Receipt, opts: PrCommentOptions = {}): string {\n const p = receipt.predicate;\n const ev = p.evidence;\n\n // Merge gate: show only findings about the merged artifact. Operator/efficiency\n // findings (loops, retries, cost) live on `receipts` / `receipts trends`; a\n // destructive op on a scratch path (`rm -rf \"$TMP\"`) isn't a merge risk. The\n // verdict is recomputed from what's shown, so it matches the visible findings\n // (the committed receipt is unchanged β re-derivation/L1 is unaffected).\n const operatorCount = p.findings.filter((f) => findingSurface(f.id) === \"operator\").length;\n const mergeFindings = p.findings.filter(\n (f) =>\n findingSurface(f.id) === \"merge\" &&\n !(f.id.startsWith(\"destructive-\") && destructiveOutsideRepo(f.title)),\n );\n const g = gradeLetter(mergeFindings.filter((f) => f.confidence >= 0.5));\n const main = mergeFindings.filter((f) => f.severity !== \"low\");\n const cats = categorize(main);\n\n const out: string[] = [];\n out.push(PR_COMMENT_MARKER);\n\n // Headline β the verdict is folded in so the status reads in one glance.\n out.push(`### π§Ύ Verified by Receipts Β· ${GRADE_DOT[g]} Grade ${g} β ${VERDICT[g]}`);\n out.push(\"\");\n out.push(EXPLAINER); // one-line plain-English \"what is this\" for first-time viewers\n out.push(\"\");\n\n // When something fired, shout: a callout + the actionable findings. Silent when clean.\n if (main.length) {\n const co = CALLOUT[g] ?? \"WARNING\";\n const what = cats.flagged.length\n ? cats.flagged.map((fl) => fl.check.label).join(\", \")\n : `${main.length} finding${main.length === 1 ? \"\" : \"s\"}`;\n out.push(`> [!${co}]`);\n out.push(\n `> **${cats.flagged.length || main.length} of ${cats.total} checks flagged:** ${what}.`,\n );\n out.push(\"\");\n const sorted = [...main].sort(\n (a, b) => ORDER[a.severity] - ORDER[b.severity] || b.score - a.score,\n );\n for (const f of sorted.slice(0, 12)) {\n out.push(findingLine(f));\n }\n if (main.length > 12) {\n out.push(`- _β¦and ${main.length - 12} more β see the full breakdown_`);\n }\n out.push(\"\");\n }\n\n // One-glance stat strip β always present, cost included.\n out.push(statStrip(cats, ev, p));\n out.push(\"\");\n\n // The single \"dig deeper\": files, claim ledger, every check, cost detail, session, trust.\n const feedback = feedbackLine(p.session.agent, g, main);\n out.push(fullBreakdown(p, ev, cats, operatorCount, opts, feedback));\n return `${out.join(\"\\n\")}\\n`;\n}\n\n/** Compact cost chip for the strip β ALWAYS this change's diff-scoped cost (an upper\n * bound), never the whole-session total. The session figure would dwarf a small PR and\n * mislead, so it never appears here; the session totals live, clearly labelled, in the\n * breakdown. Omitted only when there's no diff scope at all (a non-PR receipt). */\nfunction costStat(ev: Receipt[\"predicate\"][\"evidence\"]): string {\n return ev.diffCostUsd != null ? `π° β ${formatCostAlways(ev.diffCostUsd)} _(this change)_` : \"\";\n}\n\n/** The one-glance status strip: checks Β· files Β· tests Β· cost Β· agent. */\nfunction statStrip(\n cats: CategorizedChecks,\n ev: Receipt[\"predicate\"][\"evidence\"],\n p: Receipt[\"predicate\"],\n): string {\n const checks = cats.flagged.length\n ? `β οΈ **${cats.flagged.length}/${cats.total} flagged**`\n : `β
**${cats.total}/${cats.total} checks clear**`;\n const files = p.scope?.kind === \"diff\" ? (p.scope.files ?? []) : [];\n const parts = [checks];\n if (files.length) parts.push(`π ${files.length} file${files.length === 1 ? \"\" : \"s\"}`);\n parts.push(testsCell(ev));\n const cost = costStat(ev);\n if (cost) parts.push(cost);\n parts.push(`_${p.session.agent}_`);\n return parts.join(\" Β· \");\n}\n\n/** Changed-file block for the breakdown. Lists files when few; for a big PR, groups by\n * top-level dir with counts and tucks a capped file list in a nested `<details>` β never\n * a hundreds-of-paths dump (GitHub caps a comment at ~65 KB). */\nfunction changedFilesBlock(files: readonly string[]): string {\n if (files.length <= 12) {\n return `**Changed** ${files.map((f) => `\\`${f}\\``).join(\", \")}`;\n }\n const byDir = new Map<string, number>();\n for (const f of files) {\n const top = f.includes(\"/\") ? `${f.split(\"/\")[0]}/` : \"(root)\";\n byDir.set(top, (byDir.get(top) ?? 0) + 1);\n }\n const dirs = [...byDir.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 8)\n .map(([d, n]) => `\\`${d}\\` ${n}`)\n .join(\" Β· \");\n const moreDirs = byDir.size > 8 ? ` Β· _+${byDir.size - 8} more dirs_` : \"\";\n const sample = files\n .slice(0, 60)\n .map((f) => `\\`${f}\\``)\n .join(\", \");\n const moreFiles =\n files.length > 60 ? ` _β¦+${files.length - 60} more (full set in the receipt JSON)_` : \"\";\n return [\n `**Changed ${files.length} files** by area: ${dirs}${moreDirs}`,\n \"\",\n \"<details><summary>file list</summary>\",\n \"\",\n `${sample}${moreFiles}`,\n \"\",\n \"</details>\",\n ].join(\"\\n\");\n}\n\n/** The single deep-dive expander β everything jargon-y or large lives here. */\nfunction fullBreakdown(\n p: Receipt[\"predicate\"],\n ev: Receipt[\"predicate\"][\"evidence\"],\n cats: CategorizedChecks,\n operatorCount: number,\n opts: PrCommentOptions,\n feedback: string,\n): string {\n const inner: string[] = [];\n const files = p.scope?.kind === \"diff\" ? (p.scope.files ?? []) : [];\n if (files.length) inner.push(changedFilesBlock(files), \"\");\n const ledger = renderLedger(ev.claims ?? []);\n if (ledger) inner.push(ledger, \"\");\n inner.push(\n `**Risk checks** β ${cats.flagged.length} flagged Β· ${cats.cleared.length} clear`,\n \"\",\n catalogTable(cats),\n \"\",\n );\n const cl = diffCostLine(ev);\n if (cl) inner.push(cl);\n inner.push(`<sub>${effortLine(ev, operatorCount)}</sub>`);\n const signed = opts.signed\n ? `π Signed (Sigstore β Rekor)${opts.attestationUrl ? ` Β· [attestation](${opts.attestationUrl})` : \"\"} Β· `\n : \"\";\n inner.push(\n `<sub>${signed}Re-derivable (L1: \\`receipts verify <receipt> --transcript <t>\\`) Β· deterministic Β· 0 model calls Β· **evidence, not judgement** Β· [trust model](${TRUST_DOC_URL}).</sub>`,\n );\n inner.push(feedback);\n return [\n \"<details><summary>Full breakdown β files, checks, cost, provenance</summary>\",\n \"\",\n ...inner,\n \"\",\n \"</details>\",\n ].join(\"\\n\");\n}\n\n/**\n * The diff-cost line β ALWAYS this change's diff-scoped cost, never the session total.\n * It is an **upper bound**: it sums whole generation turns that edited a diff file, each\n * carrying its full (cache-discounted) context cost, so in a long/multi-change session it\n * over-states β hence the explicit \"upper bound\" label rather than a false-precise figure.\n * Cache reads are already priced at the discounted rate (cost.ts). Returns null only when\n * no diff cost was recorded (a non-PR receipt). The whole-session total never appears\n * here; it lives, clearly labelled, in the session-totals line.\n */\nfunction diffCostLine(ev: Receipt[\"predicate\"][\"evidence\"]): string | null {\n if (ev.diffCostUsd == null) return null;\n const turns = ev.diffTurns ?? 0;\n const t = `${turns} turn${turns === 1 ? \"\" : \"s\"}`;\n return `**Cost** β ${formatCostAlways(ev.diffCostUsd)} Β· ${formatTokens(ev.diffTokens ?? 0)} tokens Β· ${t} _(this change β upper bound; the turns that edited these files)_`;\n}\n\n/** The tests cell β structured pass/fail counts (M28) when parsed, else the boolean. */\nfunction testsCell(ev: Receipt[\"predicate\"][\"evidence\"]): string {\n const tm = ev.testMetrics;\n if (tm) {\n const seg = [\n tm.passed != null ? `${tm.passed} passed` : null,\n tm.failed ? `**${tm.failed} failed**` : null,\n tm.skipped ? `${tm.skipped} skipped` : null,\n ]\n .filter(Boolean)\n .join(\", \");\n const ok = !tm.failed;\n return `${ok ? \"β
\" : \"β οΈ\"} ${seg || tm.exitStatus}`;\n }\n return ev.testsRan ? \"β
ran\" : \"β no test run detected\";\n}\n\n/** The full check catalog as a Markdown table (flagged first), inlined in the breakdown. */\nfunction catalogTable(cats: CategorizedChecks): string {\n const sev = new Map(cats.flagged.map((fl) => [fl.check.key, fl]));\n const rows = [...cats.flagged.map((fl) => fl.check), ...cats.cleared].map((c) => {\n const fl = sev.get(c.key);\n const result = fl ? `${ICON[fl.severity]} ${fl.count} flagged` : \"β
clear\";\n return `| ${c.icon} ${c.label} | ${result} |`;\n });\n // flagged first, then cleared (both keep catalog order within each group)\n rows.sort((a, b) => (a.includes(\"β
clear\") ? 1 : 0) - (b.includes(\"β
clear\") ? 1 : 0));\n return [\"| Check | Result |\", \"| :-- | :-- |\", ...rows].join(\"\\n\");\n}\n\n/** Session-level effort, explicitly demoted β these totals are the run, not this diff. */\nfunction effortLine(ev: Receipt[\"predicate\"][\"evidence\"], operatorCount: number): string {\n const cbc = ev.commandsByClass;\n const brk = cbc\n ? ` _(${CLASS_ORDER.filter((c) => cbc[c])\n .map((c) => `${cbc[c]} ${c}`)\n .join(\", \")})_`\n : \"\";\n const op =\n operatorCount > 0\n ? ` Β· ${operatorCount} efficiency/behaviour finding${operatorCount === 1 ? \"\" : \"s\"}`\n : \"\";\n return `Session totals (the whole run, not this diff): ${ev.edits} edits Β· ${ev.commands} commands${brk} Β· ${formatCostAlways(ev.costUsd)} Β· ${formatTokens(ev.tokens.total)} tokens${op}.`;\n}\n\n/** One finding line: severity glyph + title + impact tag + path, with a \"what to check\" sub-bullet. */\nfunction findingLine(f: ReceiptFinding): string {\n const tag = f.impactLabel ? ` β ${f.impactLabel}` : \"\";\n const loc = f.filePath ? ` (\\`${f.filePath}${f.line ? `:${f.line}` : \"\"}\\`)` : \"\";\n const head = `- ${ICON[f.severity]} **${f.title}**${tag}${loc}`;\n return f.detail ? `${head}\\n β³ ${f.detail}` : head;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,oBAAoB;AAIjC,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,YAAY,mPAAkO,aAAa;AAKjQ,IAAM,sBAAsB;AAQ5B,SAAS,aACP,OACA,OACA,SACQ;AACR,QAAM,eAAe,QAAQ,SACzB,QAAQ,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,OAAO,EAAE,EAAE,KAAK,IAC3D,CAAC,2DAAsD;AAC3D,QAAM,OAAO;AAAA,IACX;AAAA,IACA,cAAc,KAAK,oBAAiB,KAAK;AAAA,IACzC;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACX,QAAM,QAAQ,QAAQ,SAAS,mBAAmB,QAAQ,CAAC,EAAE,KAAK,KAAK;AACvE,QAAM,KAAK,UAAU,mBAAmB,2BAA2B,CAAC,UAAU,mBAAmB,KAAK,CAAC,SAAS,mBAAmB,IAAI,CAAC;AACxI,QAAM,MAAM,GAAG,mBAAmB,IAAI,EAAE;AACxC,SAAO,6EAA4D,GAAG;AACxE;AAGA,IAAM,YAAyC,EAAE,GAAG,aAAM,GAAG,aAAM,GAAG,aAAM,GAAG,YAAK;AAGpF,IAAM,UAAuC;AAAA,EAC3C,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAGA,IAAM,UAA8C;AAAA,EAClD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAEA,IAAM,OAAiC,EAAE,UAAU,UAAK,MAAM,gBAAM,QAAQ,aAAM,KAAK,OAAI;AAC3F,IAAM,QAAkC,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,EAAE;AAgB3E,SAAS,gBAAgB,SAAkB,OAAyB,CAAC,GAAW;AACrF,QAAM,IAAI,QAAQ;AAClB,QAAM,KAAK,EAAE;AAOb,QAAM,gBAAgB,EAAE,SAAS,OAAO,CAAC,MAAM,eAAe,EAAE,EAAE,MAAM,UAAU,EAAE;AACpF,QAAM,gBAAgB,EAAE,SAAS;AAAA,IAC/B,CAAC,MACC,eAAe,EAAE,EAAE,MAAM,WACzB,EAAE,EAAE,GAAG,WAAW,cAAc,KAAK,uBAAuB,EAAE,KAAK;AAAA,EACvE;AACA,QAAM,IAAI,YAAY,cAAc,OAAO,CAAC,MAAM,EAAE,cAAc,GAAG,CAAC;AACtE,QAAM,OAAO,cAAc,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK;AAC7D,QAAM,OAAO,WAAW,IAAI;AAE5B,QAAM,MAAgB,CAAC;AACvB,MAAI,KAAK,iBAAiB;AAG1B,MAAI,KAAK,2CAAiC,UAAU,CAAC,CAAC,UAAU,CAAC,WAAM,QAAQ,CAAC,CAAC,EAAE;AACnF,MAAI,KAAK,EAAE;AACX,MAAI,KAAK,SAAS;AAClB,MAAI,KAAK,EAAE;AAGX,MAAI,KAAK,QAAQ;AACf,UAAM,KAAK,QAAQ,CAAC,KAAK;AACzB,UAAM,OAAO,KAAK,QAAQ,SACtB,KAAK,QAAQ,IAAI,CAAC,OAAO,GAAG,MAAM,KAAK,EAAE,KAAK,IAAI,IAClD,GAAG,KAAK,MAAM,WAAW,KAAK,WAAW,IAAI,KAAK,GAAG;AACzD,QAAI,KAAK,OAAO,EAAE,GAAG;AACrB,QAAI;AAAA,MACF,OAAO,KAAK,QAAQ,UAAU,KAAK,MAAM,OAAO,KAAK,KAAK,sBAAsB,IAAI;AAAA,IACtF;AACA,QAAI,KAAK,EAAE;AACX,UAAM,SAAS,CAAC,GAAG,IAAI,EAAE;AAAA,MACvB,CAAC,GAAG,MAAM,MAAM,EAAE,QAAQ,IAAI,MAAM,EAAE,QAAQ,KAAK,EAAE,QAAQ,EAAE;AAAA,IACjE;AACA,eAAW,KAAK,OAAO,MAAM,GAAG,EAAE,GAAG;AACnC,UAAI,KAAK,YAAY,CAAC,CAAC;AAAA,IACzB;AACA,QAAI,KAAK,SAAS,IAAI;AACpB,UAAI,KAAK,gBAAW,KAAK,SAAS,EAAE,sCAAiC;AAAA,IACvE;AACA,QAAI,KAAK,EAAE;AAAA,EACb;AAGA,MAAI,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC;AAC/B,MAAI,KAAK,EAAE;AAGX,QAAM,WAAW,aAAa,EAAE,QAAQ,OAAO,GAAG,IAAI;AACtD,MAAI,KAAK,cAAc,GAAG,IAAI,MAAM,eAAe,MAAM,QAAQ,CAAC;AAClE,SAAO,GAAG,IAAI,KAAK,IAAI,CAAC;AAAA;AAC1B;AAMA,SAAS,SAAS,IAA8C;AAC9D,SAAO,GAAG,eAAe,OAAO,oBAAQ,iBAAiB,GAAG,WAAW,CAAC,qBAAqB;AAC/F;AAGA,SAAS,UACP,MACA,IACA,GACQ;AACR,QAAM,SAAS,KAAK,QAAQ,SACxB,kBAAQ,KAAK,QAAQ,MAAM,IAAI,KAAK,KAAK,eACzC,YAAO,KAAK,KAAK,IAAI,KAAK,KAAK;AACnC,QAAM,QAAQ,EAAE,OAAO,SAAS,SAAU,EAAE,MAAM,SAAS,CAAC,IAAK,CAAC;AAClE,QAAM,QAAQ,CAAC,MAAM;AACrB,MAAI,MAAM,OAAQ,OAAM,KAAK,aAAM,MAAM,MAAM,QAAQ,MAAM,WAAW,IAAI,KAAK,GAAG,EAAE;AACtF,QAAM,KAAK,UAAU,EAAE,CAAC;AACxB,QAAM,OAAO,SAAS,EAAE;AACxB,MAAI,KAAM,OAAM,KAAK,IAAI;AACzB,QAAM,KAAK,IAAI,EAAE,QAAQ,KAAK,GAAG;AACjC,SAAO,MAAM,KAAK,QAAK;AACzB;AAKA,SAAS,kBAAkB,OAAkC;AAC3D,MAAI,MAAM,UAAU,IAAI;AACtB,WAAO,eAAe,MAAM,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,EAC/D;AACA,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,KAAK,OAAO;AACrB,UAAM,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,MAAM;AACtD,UAAM,IAAI,MAAM,MAAM,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,EAC1C;AACA,QAAM,OAAO,CAAC,GAAG,MAAM,QAAQ,CAAC,EAC7B,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,EAC/B,KAAK,QAAK;AACb,QAAM,WAAW,MAAM,OAAO,IAAI,WAAQ,MAAM,OAAO,CAAC,gBAAgB;AACxE,QAAM,SAAS,MACZ,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EACrB,KAAK,IAAI;AACZ,QAAM,YACJ,MAAM,SAAS,KAAK,YAAO,MAAM,SAAS,EAAE,0CAA0C;AACxF,SAAO;AAAA,IACL,aAAa,MAAM,MAAM,qBAAqB,IAAI,GAAG,QAAQ;AAAA,IAC7D;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,MAAM,GAAG,SAAS;AAAA,IACrB;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAGA,SAAS,cACP,GACA,IACA,MACA,eACA,MACA,UACQ;AACR,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,EAAE,OAAO,SAAS,SAAU,EAAE,MAAM,SAAS,CAAC,IAAK,CAAC;AAClE,MAAI,MAAM,OAAQ,OAAM,KAAK,kBAAkB,KAAK,GAAG,EAAE;AACzD,QAAM,SAAS,aAAa,GAAG,UAAU,CAAC,CAAC;AAC3C,MAAI,OAAQ,OAAM,KAAK,QAAQ,EAAE;AACjC,QAAM;AAAA,IACJ,0BAAqB,KAAK,QAAQ,MAAM,iBAAc,KAAK,QAAQ,MAAM;AAAA,IACzE;AAAA,IACA,aAAa,IAAI;AAAA,IACjB;AAAA,EACF;AACA,QAAM,KAAK,aAAa,EAAE;AAC1B,MAAI,GAAI,OAAM,KAAK,EAAE;AACrB,QAAM,KAAK,QAAQ,WAAW,IAAI,aAAa,CAAC,QAAQ;AACxD,QAAM,SAAS,KAAK,SAChB,2CAA+B,KAAK,iBAAiB,uBAAoB,KAAK,cAAc,MAAM,EAAE,WACpG;AACJ,QAAM;AAAA,IACJ,QAAQ,MAAM,+JAAmJ,aAAa;AAAA,EAChL;AACA,QAAM,KAAK,QAAQ;AACnB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAWA,SAAS,aAAa,IAAqD;AACzE,MAAI,GAAG,eAAe,KAAM,QAAO;AACnC,QAAM,QAAQ,GAAG,aAAa;AAC9B,QAAM,IAAI,GAAG,KAAK,QAAQ,UAAU,IAAI,KAAK,GAAG;AAChD,SAAO,mBAAc,iBAAiB,GAAG,WAAW,CAAC,SAAM,aAAa,GAAG,cAAc,CAAC,CAAC,gBAAa,CAAC;AAC3G;AAGA,SAAS,UAAU,IAA8C;AAC/D,QAAM,KAAK,GAAG;AACd,MAAI,IAAI;AACN,UAAM,MAAM;AAAA,MACV,GAAG,UAAU,OAAO,GAAG,GAAG,MAAM,YAAY;AAAA,MAC5C,GAAG,SAAS,KAAK,GAAG,MAAM,cAAc;AAAA,MACxC,GAAG,UAAU,GAAG,GAAG,OAAO,aAAa;AAAA,IACzC,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AACZ,UAAM,KAAK,CAAC,GAAG;AACf,WAAO,GAAG,KAAK,WAAM,cAAI,IAAI,OAAO,GAAG,UAAU;AAAA,EACnD;AACA,SAAO,GAAG,WAAW,eAAU;AACjC;AAGA,SAAS,aAAa,MAAiC;AACrD,QAAM,MAAM,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC;AAChE,QAAM,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,OAAO,GAAG,KAAK,GAAG,GAAG,KAAK,OAAO,EAAE,IAAI,CAAC,MAAM;AAC/E,UAAM,KAAK,IAAI,IAAI,EAAE,GAAG;AACxB,UAAM,SAAS,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC,IAAI,GAAG,KAAK,aAAa;AACjE,WAAO,KAAK,EAAE,IAAI,IAAI,EAAE,KAAK,MAAM,MAAM;AAAA,EAC3C,CAAC;AAED,OAAK,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,cAAS,IAAI,IAAI,MAAM,EAAE,SAAS,cAAS,IAAI,IAAI,EAAE;AACrF,SAAO,CAAC,sBAAsB,iBAAiB,GAAG,IAAI,EAAE,KAAK,IAAI;AACnE;AAGA,SAAS,WAAW,IAAsC,eAA+B;AACvF,QAAM,MAAM,GAAG;AACf,QAAM,MAAM,MACR,MAAM,YAAY,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,EACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,IAAI,CAAC,OACb;AACJ,QAAM,KACJ,gBAAgB,IACZ,SAAM,aAAa,gCAAgC,kBAAkB,IAAI,KAAK,GAAG,KACjF;AACN,SAAO,kDAAkD,GAAG,KAAK,eAAY,GAAG,QAAQ,YAAY,GAAG,SAAM,iBAAiB,GAAG,OAAO,CAAC,SAAM,aAAa,GAAG,OAAO,KAAK,CAAC,UAAU,EAAE;AAC1L;AAGA,SAAS,YAAY,GAA2B;AAC9C,QAAM,MAAM,EAAE,cAAc,WAAM,EAAE,WAAW,KAAK;AACpD,QAAM,MAAM,EAAE,WAAW,OAAO,EAAE,QAAQ,GAAG,EAAE,OAAO,IAAI,EAAE,IAAI,KAAK,EAAE,QAAQ;AAC/E,QAAM,OAAO,KAAK,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,KAAK,GAAG,GAAG,GAAG;AAC7D,SAAO,EAAE,SAAS,GAAG,IAAI;AAAA,WAAS,EAAE,MAAM,KAAK;AACjD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/report/prComment.ts"],"sourcesContent":["import type { Severity } from \"../findings/findings.js\";\nimport { formatCostAlways, formatTokens } from \"../findings/format.js\";\nimport { type GradeLetter, gradeLetter } from \"../findings/grade.js\";\nimport { destructiveOutsideRepo, findingSurface } from \"../findings/surface.js\";\nimport type { Receipt, ReceiptFinding } from \"../receipt/build.js\";\nimport { type CategorizedChecks, categorize } from \"./checks.js\";\nimport { renderLedger } from \"./ledger.js\";\n\n// command classes ordered most-notable first, for the effort breakdown.\nconst CLASS_ORDER = [\n \"mutating\",\n \"vcs-history\",\n \"transport\",\n \"test\",\n \"build\",\n \"read-only\",\n \"opaque\",\n];\n\n/** Stable marker so the Action can find and update its own comment. */\nexport const PR_COMMENT_MARKER = \"<!-- verified-by-receipts -->\";\n\n// Canonical docs (absolute so the links work from any repo's PR comment) + the one-line\n// plain-English explainer for first-time viewers. Kept identical in action/verified-by.mjs.\nconst TRUST_DOC_URL = \"https://github.com/AltimateAI/altimate-receipts/blob/main/docs/trust.md\";\nconst ABOUT_DOC_URL = \"https://github.com/AltimateAI/altimate-receipts/blob/main/docs/problems.md\";\nconst EXPLAINER = `<sub>π€ A deterministic check that reads the coding agent's **own transcript** and reports what it changed, ran, and claimed on this PR β evidence, **not a code-quality verdict**. New here? [What the grade & checks mean βΊ](${ABOUT_DOC_URL})</sub>`;\n// Dogfooding feedback channel β a prefilled issue against the receipts repo so a false\n// positive (the thing that kills the near-zero-FP trust pitch) is one click to report and\n// collects centrally. The reaction ask uses GitHub's native comment reactions (queryable).\n// Kept identical in action/verified-by.mjs.\nconst FEEDBACK_ISSUE_BASE = \"https://github.com/AltimateAI/altimate-receipts/issues/new\";\n\n/**\n * Build the feedback footer with a **prefilled** false-positive issue link β title from\n * the top flagged finding, body pre-populated with the PR link (a `__PR_URL__` token the\n * Action substitutes, like `__ATTESTATION_URL__`), agent, grade, and the flagged\n * finding(s) by title+id β so the reporter only writes *why*. Identical in both renderers.\n */\nfunction feedbackLine(\n agent: string,\n grade: GradeLetter,\n flagged: readonly ReceiptFinding[],\n): string {\n const findingLines = flagged.length\n ? flagged.slice(0, 8).map((f) => `- ${f.title} (\\`${f.id}\\`)`)\n : [\"- (nothing was flagged β describe what you expected)\"];\n const body = [\n \"**Repo / PR:** __PR_URL__\",\n `**Agent:** ${agent} Β· **Grade:** ${grade}`,\n \"\",\n \"**Flagged finding(s) being disputed:**\",\n ...findingLines,\n \"\",\n \"**Why this is a false positive / noise:**\",\n \"_(your notes β what should it have done instead?)_\",\n ].join(\"\\n\");\n const title = flagged.length ? `False positive: ${flagged[0].title}` : \"Receipts feedback\";\n const qs = `labels=${encodeURIComponent(\"false-positive,dogfooding\")}&title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`;\n const url = `${FEEDBACK_ISSUE_BASE}?${qs}`;\n return `<sub>π¬ Wrong call or noise? **[Report it β prefilled βΊ](${url})** (only the \"why\" is left to write) Β· or use the π reaction button on this comment to vote π / π. It tunes the checks.</sub>`;\n}\n\n/** Traffic-light dot per grade β a glanceable status glyph in the headline. */\nconst GRADE_DOT: Record<GradeLetter, string> = { A: \"π’\", B: \"π‘\", C: \"π \", F: \"π΄\" };\n\n/** One-line verdict, scaled to the grade. */\nconst VERDICT: Record<GradeLetter, string> = {\n A: \"Safe to merge\",\n B: \"Minor things to check\",\n C: \"Needs a close review\",\n F: \"Do not merge without review\",\n};\n\n/** GitHub alert kind used to frame a flagged change β none on a clean run (no alarm). */\nconst CALLOUT: Record<GradeLetter, string | null> = {\n A: null,\n B: \"NOTE\",\n C: \"WARNING\",\n F: \"CAUTION\",\n};\n\nconst ICON: Record<Severity, string> = { critical: \"β\", high: \"β οΈ\", medium: \"π\", low: \"Β·\" };\nconst ORDER: Record<Severity, number> = { critical: 0, high: 1, medium: 2, low: 3 };\n\nexport interface PrCommentOptions {\n /** link to the signed attestation / Rekor entry, when available */\n attestationUrl?: string;\n /** whether the Receipt was Sigstore-signed in this run */\n signed?: boolean;\n /** M68 β the verifier's version to compare the receipt's `generator.version` against;\n * only the CI Action sets this. When older, a muted upgrade line is appended. The local\n * CLI passes nothing (no compare target β no line β it can't know a remote version). */\n compareVersion?: string;\n}\n\n/**\n * Render the \"Verified-by: Receipts\" PR comment (Markdown). The Receipt is assumed\n * already redacted. Two zones (progressive disclosure): an always-visible verdict +\n * what-changed + the flagged findings, then the full 14-check catalog collapsed in a\n * `<details>` (open only when something fired). Effort/cost is demoted to a muted\n * footer β it's session-level, not this diff. Includes the trust caveat (SPEC-0005 R6).\n */\nexport function renderPrComment(receipt: Receipt, opts: PrCommentOptions = {}): string {\n const p = receipt.predicate;\n const ev = p.evidence;\n\n // Merge gate: show only findings about the merged artifact. Operator/efficiency\n // findings (loops, retries, cost) live on `receipts` / `receipts trends`; a\n // destructive op on a scratch path (`rm -rf \"$TMP\"`) isn't a merge risk. The\n // verdict is recomputed from what's shown, so it matches the visible findings\n // (the committed receipt is unchanged β re-derivation/L1 is unaffected).\n const operatorCount = p.findings.filter((f) => findingSurface(f.id) === \"operator\").length;\n const mergeFindings = p.findings.filter(\n (f) =>\n findingSurface(f.id) === \"merge\" &&\n !(f.id.startsWith(\"destructive-\") && destructiveOutsideRepo(f.title)),\n );\n const g = gradeLetter(mergeFindings.filter((f) => f.confidence >= 0.5));\n const main = mergeFindings.filter((f) => f.severity !== \"low\");\n const cats = categorize(main);\n\n const out: string[] = [];\n out.push(PR_COMMENT_MARKER);\n\n // Headline β the verdict is folded in so the status reads in one glance.\n out.push(`### π§Ύ Verified by Receipts Β· ${GRADE_DOT[g]} Grade ${g} β ${VERDICT[g]}`);\n out.push(\"\");\n out.push(EXPLAINER); // one-line plain-English \"what is this\" for first-time viewers\n out.push(\"\");\n\n // When something fired, shout: a callout + the actionable findings. Silent when clean.\n if (main.length) {\n const co = CALLOUT[g] ?? \"WARNING\";\n const what = cats.flagged.length\n ? cats.flagged.map((fl) => fl.check.label).join(\", \")\n : `${main.length} finding${main.length === 1 ? \"\" : \"s\"}`;\n out.push(`> [!${co}]`);\n out.push(\n `> **${cats.flagged.length || main.length} of ${cats.total} checks flagged:** ${what}.`,\n );\n out.push(\"\");\n const sorted = [...main].sort(\n (a, b) => ORDER[a.severity] - ORDER[b.severity] || b.score - a.score,\n );\n for (const f of sorted.slice(0, 12)) {\n out.push(findingLine(f));\n }\n if (main.length > 12) {\n out.push(`- _β¦and ${main.length - 12} more β see the full breakdown_`);\n }\n out.push(\"\");\n }\n\n // One-glance stat strip β always present, cost included.\n out.push(statStrip(cats, ev, p));\n out.push(\"\");\n\n // The single \"dig deeper\": files, claim ledger, every check, cost detail, session, trust.\n const feedback = feedbackLine(p.session.agent, g, main);\n out.push(fullBreakdown(p, ev, cats, operatorCount, opts, feedback));\n return `${out.join(\"\\n\")}\\n`;\n}\n\n/** Compact cost chip for the strip β ALWAYS this change's diff-scoped cost (an upper\n * bound), never the whole-session total. The session figure would dwarf a small PR and\n * mislead, so it never appears here; the session totals live, clearly labelled, in the\n * breakdown. Omitted only when there's no diff scope at all (a non-PR receipt). */\nfunction costStat(ev: Receipt[\"predicate\"][\"evidence\"]): string {\n return ev.diffCostUsd != null ? `π° β ${formatCostAlways(ev.diffCostUsd)} _(this change)_` : \"\";\n}\n\n/** The one-glance status strip: checks Β· files Β· tests Β· cost Β· agent. */\nfunction statStrip(\n cats: CategorizedChecks,\n ev: Receipt[\"predicate\"][\"evidence\"],\n p: Receipt[\"predicate\"],\n): string {\n const checks = cats.flagged.length\n ? `β οΈ **${cats.flagged.length}/${cats.total} flagged**`\n : `β
**${cats.total}/${cats.total} checks clear**`;\n const files = p.scope?.kind === \"diff\" ? (p.scope.files ?? []) : [];\n const parts = [checks];\n if (files.length) parts.push(`π ${files.length} file${files.length === 1 ? \"\" : \"s\"}`);\n parts.push(testsCell(ev));\n const cost = costStat(ev);\n if (cost) parts.push(cost);\n parts.push(`_${p.session.agent}_`);\n return parts.join(\" Β· \");\n}\n\n/** Changed-file block for the breakdown. Lists files when few; for a big PR, groups by\n * top-level dir with counts and tucks a capped file list in a nested `<details>` β never\n * a hundreds-of-paths dump (GitHub caps a comment at ~65 KB). */\nfunction changedFilesBlock(files: readonly string[]): string {\n if (files.length <= 12) {\n return `**Changed** ${files.map((f) => `\\`${f}\\``).join(\", \")}`;\n }\n const byDir = new Map<string, number>();\n for (const f of files) {\n const top = f.includes(\"/\") ? `${f.split(\"/\")[0]}/` : \"(root)\";\n byDir.set(top, (byDir.get(top) ?? 0) + 1);\n }\n const dirs = [...byDir.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 8)\n .map(([d, n]) => `\\`${d}\\` ${n}`)\n .join(\" Β· \");\n const moreDirs = byDir.size > 8 ? ` Β· _+${byDir.size - 8} more dirs_` : \"\";\n const sample = files\n .slice(0, 60)\n .map((f) => `\\`${f}\\``)\n .join(\", \");\n const moreFiles =\n files.length > 60 ? ` _β¦+${files.length - 60} more (full set in the receipt JSON)_` : \"\";\n return [\n `**Changed ${files.length} files** by area: ${dirs}${moreDirs}`,\n \"\",\n \"<details><summary>file list</summary>\",\n \"\",\n `${sample}${moreFiles}`,\n \"\",\n \"</details>\",\n ].join(\"\\n\");\n}\n\n// M68 β upgrade-skew notice (MIRRORS action/verified-by.mjs). Plain x.y.z compare (no\n// pre-release handling β receipts ships plain semver); false on any parse failure so a\n// malformed version never nags.\nfunction parseSemver(v?: string): [number, number, number] | null {\n const m = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(String(v ?? \"\").trim());\n return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;\n}\nfunction semverLt(a?: string, b?: string): boolean {\n const pa = parseSemver(a);\n const pb = parseSemver(b);\n if (!pa || !pb) return false;\n for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pa[i] < pb[i];\n return false;\n}\n/** One muted footer line when the receipt was made by a CLI older than `compareVer` (the\n * verifier's version). A mechanical fact (X < Y), never a finding and never a grade change;\n * null when current/ahead/equal/unparseable or when there is no compare target. */\nexport function upgradeHint(receiptVer?: string, compareVer?: string): string | null {\n if (!compareVer || !semverLt(receiptVer, compareVer)) return null;\n return `<sub>π§Ύ Receipt generated by \\`receipts@${receiptVer}\\` Β· this check runs \\`@${compareVer}\\` β \\`npm i -g altimate-receipts@latest\\` (or \\`npx altimate-receipts@latest\\`) for the newer checks.</sub>`;\n}\n\n/** The single deep-dive expander β everything jargon-y or large lives here. */\nfunction fullBreakdown(\n p: Receipt[\"predicate\"],\n ev: Receipt[\"predicate\"][\"evidence\"],\n cats: CategorizedChecks,\n operatorCount: number,\n opts: PrCommentOptions,\n feedback: string,\n): string {\n const inner: string[] = [];\n const files = p.scope?.kind === \"diff\" ? (p.scope.files ?? []) : [];\n if (files.length) inner.push(changedFilesBlock(files), \"\");\n const ledger = renderLedger(ev.claims ?? []);\n if (ledger) inner.push(ledger, \"\");\n inner.push(\n `**Risk checks** β ${cats.flagged.length} flagged Β· ${cats.cleared.length} clear`,\n \"\",\n catalogTable(cats),\n \"\",\n );\n const cl = diffCostLine(ev);\n if (cl) inner.push(cl);\n inner.push(`<sub>${effortLine(ev, operatorCount)}</sub>`);\n const signed = opts.signed\n ? `π Signed (Sigstore β Rekor)${opts.attestationUrl ? ` Β· [attestation](${opts.attestationUrl})` : \"\"} Β· `\n : \"\";\n inner.push(\n `<sub>${signed}Re-derivable (L1: \\`receipts verify <receipt> --transcript <t>\\`) Β· deterministic Β· 0 model calls Β· **evidence, not judgement** Β· [trust model](${TRUST_DOC_URL}).</sub>`,\n );\n const hint = upgradeHint(p.generator?.version, opts.compareVersion);\n if (hint) inner.push(hint);\n inner.push(feedback);\n return [\n \"<details><summary>Full breakdown β files, checks, cost, provenance</summary>\",\n \"\",\n ...inner,\n \"\",\n \"</details>\",\n ].join(\"\\n\");\n}\n\n/**\n * The diff-cost line β ALWAYS this change's diff-scoped cost, never the session total.\n * It is an **upper bound**: it sums whole generation turns that edited a diff file, each\n * carrying its full (cache-discounted) context cost, so in a long/multi-change session it\n * over-states β hence the explicit \"upper bound\" label rather than a false-precise figure.\n * Cache reads are already priced at the discounted rate (cost.ts). Returns null only when\n * no diff cost was recorded (a non-PR receipt). The whole-session total never appears\n * here; it lives, clearly labelled, in the session-totals line.\n */\nfunction diffCostLine(ev: Receipt[\"predicate\"][\"evidence\"]): string | null {\n if (ev.diffCostUsd == null) return null;\n const turns = ev.diffTurns ?? 0;\n const t = `${turns} turn${turns === 1 ? \"\" : \"s\"}`;\n return `**Cost** β ${formatCostAlways(ev.diffCostUsd)} Β· ${formatTokens(ev.diffTokens ?? 0)} tokens Β· ${t} _(this change β upper bound; the turns that edited these files)_`;\n}\n\n/** The tests cell β structured pass/fail counts (M28) when parsed, else the boolean. */\nfunction testsCell(ev: Receipt[\"predicate\"][\"evidence\"]): string {\n const tm = ev.testMetrics;\n if (tm) {\n const seg = [\n tm.passed != null ? `${tm.passed} passed` : null,\n tm.failed ? `**${tm.failed} failed**` : null,\n tm.skipped ? `${tm.skipped} skipped` : null,\n ]\n .filter(Boolean)\n .join(\", \");\n const ok = !tm.failed;\n return `${ok ? \"β
\" : \"β οΈ\"} ${seg || tm.exitStatus}`;\n }\n return ev.testsRan ? \"β
ran\" : \"β no test run detected\";\n}\n\n/** The full check catalog as a Markdown table (flagged first), inlined in the breakdown. */\nfunction catalogTable(cats: CategorizedChecks): string {\n const sev = new Map(cats.flagged.map((fl) => [fl.check.key, fl]));\n const rows = [...cats.flagged.map((fl) => fl.check), ...cats.cleared].map((c) => {\n const fl = sev.get(c.key);\n const result = fl ? `${ICON[fl.severity]} ${fl.count} flagged` : \"β
clear\";\n return `| ${c.icon} ${c.label} | ${result} |`;\n });\n // flagged first, then cleared (both keep catalog order within each group)\n rows.sort((a, b) => (a.includes(\"β
clear\") ? 1 : 0) - (b.includes(\"β
clear\") ? 1 : 0));\n return [\"| Check | Result |\", \"| :-- | :-- |\", ...rows].join(\"\\n\");\n}\n\n/** Session-level effort, explicitly demoted β these totals are the run, not this diff. */\nfunction effortLine(ev: Receipt[\"predicate\"][\"evidence\"], operatorCount: number): string {\n const cbc = ev.commandsByClass;\n const brk = cbc\n ? ` _(${CLASS_ORDER.filter((c) => cbc[c])\n .map((c) => `${cbc[c]} ${c}`)\n .join(\", \")})_`\n : \"\";\n const op =\n operatorCount > 0\n ? ` Β· ${operatorCount} efficiency/behaviour finding${operatorCount === 1 ? \"\" : \"s\"}`\n : \"\";\n return `Session totals (the whole run, not this diff): ${ev.edits} edits Β· ${ev.commands} commands${brk} Β· ${formatCostAlways(ev.costUsd)} Β· ${formatTokens(ev.tokens.total)} tokens${op}.`;\n}\n\n/** One finding line: severity glyph + title + impact tag + path, with a \"what to check\" sub-bullet. */\nfunction findingLine(f: ReceiptFinding): string {\n const tag = f.impactLabel ? ` β ${f.impactLabel}` : \"\";\n const loc = f.filePath ? ` (\\`${f.filePath}${f.line ? `:${f.line}` : \"\"}\\`)` : \"\";\n const head = `- ${ICON[f.severity]} **${f.title}**${tag}${loc}`;\n return f.detail ? `${head}\\n β³ ${f.detail}` : head;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED