altimate-receipts 0.6.2 → 0.7.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/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  renderShareMarkdown,
15
15
  sliceByBranch,
16
16
  toDsseEnvelope
17
- } from "./chunk-5R6W4W4R.js";
17
+ } from "./chunk-FTKMO26V.js";
18
18
  import {
19
19
  PREDICATE_TYPE,
20
20
  adapterFor,
@@ -48,7 +48,7 @@ import {
48
48
  upsertGuardrailsSection,
49
49
  validateReceiptShape,
50
50
  verifyBundle
51
- } from "./chunk-72Y2GS7I.js";
51
+ } from "./chunk-ZORGM2DA.js";
52
52
 
53
53
  // src/report/prComment.ts
54
54
  var CLASS_ORDER = [
@@ -139,8 +139,14 @@ function renderPrComment(receipt, opts = {}) {
139
139
  return `${out.join("\n")}
140
140
  `;
141
141
  }
142
- function costStat(ev) {
143
- return ev.diffCostUsd != null ? `\u{1F4B0} \u2248 ${formatCostAlways(ev.diffCostUsd)} _(this change)_` : "";
142
+ function costStat(ev, pr) {
143
+ if (ev.diffCostUsd == null) {
144
+ return "";
145
+ }
146
+ if (pr && pr.sessions > 1) {
147
+ return `\u{1F4B0} \u2248 ${formatCostAlways(pr.totalUsd)} _(this PR, ${pr.sessions} sessions)_`;
148
+ }
149
+ return `\u{1F4B0} \u2248 ${formatCostAlways(ev.diffCostUsd)} _(this change)_`;
144
150
  }
145
151
  function statStrip(cats, ev, p) {
146
152
  const checks = cats.flagged.length ? `\u26A0\uFE0F **${cats.flagged.length}/${cats.total} flagged**` : `\u2705 **${cats.total}/${cats.total} checks clear**`;
@@ -148,7 +154,7 @@ function statStrip(cats, ev, p) {
148
154
  const parts = [checks];
149
155
  if (files.length) parts.push(`\u{1F4C4} ${files.length} file${files.length === 1 ? "" : "s"}`);
150
156
  parts.push(testsCell(ev));
151
- const cost = costStat(ev);
157
+ const cost = costStat(ev, p.prEffort);
152
158
  if (cost) parts.push(cost);
153
159
  parts.push(`_${p.session.agent}_`);
154
160
  return parts.join(" \xB7 ");
@@ -203,7 +209,7 @@ function fullBreakdown(p, ev, cats, operatorCount, opts, feedback) {
203
209
  catalogTable(cats),
204
210
  ""
205
211
  );
206
- const cl = diffCostLine(ev);
212
+ const cl = diffCostLine(ev, p.prEffort);
207
213
  if (cl) inner.push(cl);
208
214
  inner.push(`<sub>${effortLine(ev, operatorCount)}</sub>`);
209
215
  const signed = opts.signed ? `\u{1F50F} Signed (Sigstore \u2192 Rekor)${opts.attestationUrl ? ` \xB7 [attestation](${opts.attestationUrl})` : ""} \xB7 ` : "";
@@ -221,11 +227,15 @@ function fullBreakdown(p, ev, cats, operatorCount, opts, feedback) {
221
227
  "</details>"
222
228
  ].join("\n");
223
229
  }
224
- function diffCostLine(ev) {
230
+ function diffCostLine(ev, pr) {
225
231
  if (ev.diffCostUsd == null) return null;
226
232
  const turns = ev.diffTurns ?? 0;
227
233
  const t = `${turns} turn${turns === 1 ? "" : "s"}`;
228
- return `**Cost** \u2248 ${formatCostAlways(ev.diffCostUsd)} \xB7 ${formatTokens(ev.diffTokens ?? 0)} tokens \xB7 ${t} _(this change \u2014 upper bound; the turns that edited these files)_`;
234
+ const push = `\u2248 ${formatCostAlways(ev.diffCostUsd)} \xB7 ${formatTokens(ev.diffTokens ?? 0)} tokens \xB7 ${t}`;
235
+ if (pr && pr.sessions > 1) {
236
+ return `**Cost** \u2248 ${formatCostAlways(pr.totalUsd)} _(this PR, ${pr.sessions} sessions)_ \xB7 this push ${push} _(upper bound; the turns that edited these files)_`;
237
+ }
238
+ return `**Cost** ${push} _(this change \u2014 upper bound; the turns that edited these files)_`;
229
239
  }
230
240
  function testsCell(ev) {
231
241
  const tm = ev.testMetrics;
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 /** 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":[]}
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(\n ev: Receipt[\"predicate\"][\"evidence\"],\n pr?: Receipt[\"predicate\"][\"prEffort\"],\n): string {\n if (ev.diffCostUsd == null) {\n return \"\";\n }\n // SPEC-0072 R4: a PR built across several sessions leads with its cumulative cost.\n if (pr && pr.sessions > 1) {\n return `💰 ≈ ${formatCostAlways(pr.totalUsd)} _(this PR, ${pr.sessions} sessions)_`;\n }\n return `💰 ≈ ${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, p.prEffort);\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, p.prEffort);\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(\n ev: Receipt[\"predicate\"][\"evidence\"],\n pr?: Receipt[\"predicate\"][\"prEffort\"],\n): string | null {\n if (ev.diffCostUsd == null) return null;\n const turns = ev.diffTurns ?? 0;\n const t = `${turns} turn${turns === 1 ? \"\" : \"s\"}`;\n const push = `≈ ${formatCostAlways(ev.diffCostUsd)} · ${formatTokens(ev.diffTokens ?? 0)} tokens · ${t}`;\n // SPEC-0072 R4: cumulative first when several sessions built this PR.\n if (pr && pr.sessions > 1) {\n return `**Cost** ≈ ${formatCostAlways(pr.totalUsd)} _(this PR, ${pr.sessions} sessions)_ · this push ${push} _(upper bound; the turns that edited these files)_`;\n }\n return `**Cost** ${push} _(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":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  computeTrends,
4
4
  deriveTargets
5
- } from "../chunk-OQJHGUIN.js";
5
+ } from "../chunk-MSPULJEI.js";
6
6
  import {
7
7
  agentIds,
8
8
  buildReceipt,
@@ -16,7 +16,7 @@ import {
16
16
  renderCard,
17
17
  selectSummary,
18
18
  verifyBundle
19
- } from "../chunk-72Y2GS7I.js";
19
+ } from "../chunk-ZORGM2DA.js";
20
20
 
21
21
  // src/mcp/server.ts
22
22
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "altimate-receipts",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Agent-work verification, not code review — a deterministic, cross-agent Report Card of what your coding agent actually did.",
5
5
  "keywords": [
6
6
  "ai",
@@ -7,8 +7,12 @@
7
7
  "required": ["_type", "subject", "predicateType", "predicate"],
8
8
  "additionalProperties": false,
9
9
  "properties": {
10
- "_type": { "const": "https://in-toto.io/Statement/v1" },
11
- "predicateType": { "const": "https://receipts.dev/agent-execution/v1" },
10
+ "_type": {
11
+ "const": "https://in-toto.io/Statement/v1"
12
+ },
13
+ "predicateType": {
14
+ "const": "https://receipts.dev/agent-execution/v1"
15
+ },
12
16
  "subject": {
13
17
  "type": "array",
14
18
  "minItems": 1,
@@ -17,23 +21,35 @@
17
21
  "required": ["name", "digest"],
18
22
  "additionalProperties": false,
19
23
  "properties": {
20
- "name": { "type": "string", "minLength": 1 },
24
+ "name": {
25
+ "type": "string",
26
+ "minLength": 1
27
+ },
21
28
  "digest": {
22
29
  "type": "object",
23
30
  "required": ["sha256"],
24
31
  "additionalProperties": false,
25
32
  "properties": {
26
- "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
33
+ "sha256": {
34
+ "type": "string",
35
+ "pattern": "^[a-f0-9]{64}$"
36
+ }
27
37
  }
28
38
  }
29
39
  }
30
40
  }
31
41
  },
32
- "predicate": { "$ref": "#/$defs/predicate" }
42
+ "predicate": {
43
+ "$ref": "#/$defs/predicate"
44
+ }
33
45
  },
34
46
  "$defs": {
35
- "severity": { "enum": ["critical", "high", "medium", "low"] },
36
- "grade": { "enum": ["A", "B", "C", "F"] },
47
+ "severity": {
48
+ "enum": ["critical", "high", "medium", "low"]
49
+ },
50
+ "grade": {
51
+ "enum": ["A", "B", "C", "F"]
52
+ },
37
53
  "predicate": {
38
54
  "type": "object",
39
55
  "required": ["session", "grade", "evidence", "findings", "generator"],
@@ -44,15 +60,31 @@
44
60
  "required": ["agent"],
45
61
  "additionalProperties": false,
46
62
  "properties": {
47
- "agent": { "type": "string", "minLength": 1 },
48
- "model": { "type": "string" },
49
- "title": { "type": "string" },
50
- "startedAt": { "type": "integer" },
51
- "endedAt": { "type": "integer" },
52
- "durationMs": { "type": "integer", "minimum": 0 }
63
+ "agent": {
64
+ "type": "string",
65
+ "minLength": 1
66
+ },
67
+ "model": {
68
+ "type": "string"
69
+ },
70
+ "title": {
71
+ "type": "string"
72
+ },
73
+ "startedAt": {
74
+ "type": "integer"
75
+ },
76
+ "endedAt": {
77
+ "type": "integer"
78
+ },
79
+ "durationMs": {
80
+ "type": "integer",
81
+ "minimum": 0
82
+ }
53
83
  }
54
84
  },
55
- "grade": { "$ref": "#/$defs/grade" },
85
+ "grade": {
86
+ "$ref": "#/$defs/grade"
87
+ },
56
88
  "evidence": {
57
89
  "type": "object",
58
90
  "required": [
@@ -68,31 +100,70 @@
68
100
  ],
69
101
  "additionalProperties": false,
70
102
  "properties": {
71
- "filesChanged": { "type": "integer", "minimum": 0 },
72
- "edits": { "type": "integer", "minimum": 0 },
73
- "commands": { "type": "integer", "minimum": 0 },
74
- "reads": { "type": "integer", "minimum": 0 },
75
- "destructiveOps": { "type": "integer", "minimum": 0 },
76
- "testsRan": { "type": "boolean" },
103
+ "filesChanged": {
104
+ "type": "integer",
105
+ "minimum": 0
106
+ },
107
+ "edits": {
108
+ "type": "integer",
109
+ "minimum": 0
110
+ },
111
+ "commands": {
112
+ "type": "integer",
113
+ "minimum": 0
114
+ },
115
+ "reads": {
116
+ "type": "integer",
117
+ "minimum": 0
118
+ },
119
+ "destructiveOps": {
120
+ "type": "integer",
121
+ "minimum": 0
122
+ },
123
+ "testsRan": {
124
+ "type": "boolean"
125
+ },
77
126
  "tokens": {
78
127
  "type": "object",
79
128
  "required": ["input", "output", "cacheRead", "cacheWrite", "total"],
80
129
  "additionalProperties": false,
81
130
  "properties": {
82
- "input": { "type": "integer", "minimum": 0 },
83
- "output": { "type": "integer", "minimum": 0 },
84
- "cacheRead": { "type": "integer", "minimum": 0 },
85
- "cacheWrite": { "type": "integer", "minimum": 0 },
131
+ "input": {
132
+ "type": "integer",
133
+ "minimum": 0
134
+ },
135
+ "output": {
136
+ "type": "integer",
137
+ "minimum": 0
138
+ },
139
+ "cacheRead": {
140
+ "type": "integer",
141
+ "minimum": 0
142
+ },
143
+ "cacheWrite": {
144
+ "type": "integer",
145
+ "minimum": 0
146
+ },
86
147
  "reasoning": {
87
148
  "type": "integer",
88
149
  "minimum": 0,
89
150
  "description": "reasoning/thinking tokens (M41); optional, already included in total"
90
151
  },
91
- "total": { "type": "integer", "minimum": 0 }
152
+ "total": {
153
+ "type": "integer",
154
+ "minimum": 0
155
+ }
92
156
  }
93
157
  },
94
- "costUsd": { "type": "number", "minimum": 0 },
95
- "cacheHitRatio": { "type": "number", "minimum": 0, "maximum": 1 },
158
+ "costUsd": {
159
+ "type": "number",
160
+ "minimum": 0
161
+ },
162
+ "cacheHitRatio": {
163
+ "type": "number",
164
+ "minimum": 0,
165
+ "maximum": 1
166
+ },
96
167
  "cacheWriteRatio": {
97
168
  "type": "number",
98
169
  "minimum": 0,
@@ -116,7 +187,10 @@
116
187
  "commandsByClass": {
117
188
  "type": "object",
118
189
  "description": "count of command spans by descriptive class (M31); optional",
119
- "additionalProperties": { "type": "integer", "minimum": 0 },
190
+ "additionalProperties": {
191
+ "type": "integer",
192
+ "minimum": 0
193
+ },
120
194
  "propertyNames": {
121
195
  "enum": [
122
196
  "read-only",
@@ -135,14 +209,37 @@
135
209
  "required": ["runner", "exitStatus"],
136
210
  "additionalProperties": false,
137
211
  "properties": {
138
- "runner": { "type": "string" },
139
- "passed": { "type": "integer", "minimum": 0 },
140
- "failed": { "type": "integer", "minimum": 0 },
141
- "skipped": { "type": "integer", "minimum": 0 },
142
- "total": { "type": "integer", "minimum": 0 },
143
- "exitStatus": { "enum": ["passed", "failed", "unknown"] },
144
- "durationMs": { "type": "integer", "minimum": 0 },
145
- "coveragePct": { "type": "number", "minimum": 0, "maximum": 100 }
212
+ "runner": {
213
+ "type": "string"
214
+ },
215
+ "passed": {
216
+ "type": "integer",
217
+ "minimum": 0
218
+ },
219
+ "failed": {
220
+ "type": "integer",
221
+ "minimum": 0
222
+ },
223
+ "skipped": {
224
+ "type": "integer",
225
+ "minimum": 0
226
+ },
227
+ "total": {
228
+ "type": "integer",
229
+ "minimum": 0
230
+ },
231
+ "exitStatus": {
232
+ "enum": ["passed", "failed", "unknown"]
233
+ },
234
+ "durationMs": {
235
+ "type": "integer",
236
+ "minimum": 0
237
+ },
238
+ "coveragePct": {
239
+ "type": "number",
240
+ "minimum": 0,
241
+ "maximum": 100
242
+ }
146
243
  }
147
244
  },
148
245
  "toolErrorRate": {
@@ -153,16 +250,29 @@
153
250
  "required": ["invocations", "errored", "errorRate"],
154
251
  "additionalProperties": false,
155
252
  "properties": {
156
- "invocations": { "type": "integer", "minimum": 0 },
157
- "errored": { "type": "integer", "minimum": 0 },
158
- "errorRate": { "type": "number", "minimum": 0, "maximum": 1 }
253
+ "invocations": {
254
+ "type": "integer",
255
+ "minimum": 0
256
+ },
257
+ "errored": {
258
+ "type": "integer",
259
+ "minimum": 0
260
+ },
261
+ "errorRate": {
262
+ "type": "number",
263
+ "minimum": 0,
264
+ "maximum": 1
265
+ }
159
266
  }
160
267
  }
161
268
  },
162
269
  "finishReasons": {
163
270
  "type": "object",
164
271
  "description": "count of assistant turns by normalized API stop reason (M35); optional",
165
- "additionalProperties": { "type": "integer", "minimum": 0 },
272
+ "additionalProperties": {
273
+ "type": "integer",
274
+ "minimum": 0
275
+ },
166
276
  "propertyNames": {
167
277
  "enum": ["stop", "length", "content_filter", "tool_use", "error", "unknown"]
168
278
  }
@@ -178,8 +288,13 @@
178
288
  "kind": {
179
289
  "enum": ["committed-pushed", "ran-tests"]
180
290
  },
181
- "status": { "enum": ["PASS", "UNVERIFIED"] },
182
- "evidence": { "type": "string", "minLength": 1 }
291
+ "status": {
292
+ "enum": ["PASS", "UNVERIFIED"]
293
+ },
294
+ "evidence": {
295
+ "type": "string",
296
+ "minLength": 1
297
+ }
183
298
  }
184
299
  }
185
300
  }
@@ -192,16 +307,42 @@
192
307
  "required": ["id", "severity", "title", "confidence", "score"],
193
308
  "additionalProperties": false,
194
309
  "properties": {
195
- "id": { "type": "string", "minLength": 1 },
196
- "severity": { "$ref": "#/$defs/severity" },
197
- "title": { "type": "string", "minLength": 1 },
198
- "detail": { "type": "string" },
199
- "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
200
- "score": { "type": "number", "minimum": 0 },
201
- "impactLabel": { "type": "string" },
202
- "filePath": { "type": "string" },
203
- "line": { "type": "integer", "minimum": 1 },
204
- "evidenceRef": { "type": "string" }
310
+ "id": {
311
+ "type": "string",
312
+ "minLength": 1
313
+ },
314
+ "severity": {
315
+ "$ref": "#/$defs/severity"
316
+ },
317
+ "title": {
318
+ "type": "string",
319
+ "minLength": 1
320
+ },
321
+ "detail": {
322
+ "type": "string"
323
+ },
324
+ "confidence": {
325
+ "type": "number",
326
+ "minimum": 0,
327
+ "maximum": 1
328
+ },
329
+ "score": {
330
+ "type": "number",
331
+ "minimum": 0
332
+ },
333
+ "impactLabel": {
334
+ "type": "string"
335
+ },
336
+ "filePath": {
337
+ "type": "string"
338
+ },
339
+ "line": {
340
+ "type": "integer",
341
+ "minimum": 1
342
+ },
343
+ "evidenceRef": {
344
+ "type": "string"
345
+ }
205
346
  }
206
347
  }
207
348
  },
@@ -215,7 +356,9 @@
215
356
  "required": ["kind"],
216
357
  "additionalProperties": false,
217
358
  "properties": {
218
- "kind": { "enum": ["diff", "branch", "session"] },
359
+ "kind": {
360
+ "enum": ["diff", "branch", "session"]
361
+ },
219
362
  "base": {
220
363
  "type": "string",
221
364
  "description": "diff: the ref the diff was taken against (e.g. origin/main)."
@@ -223,11 +366,21 @@
223
366
  "files": {
224
367
  "type": "array",
225
368
  "description": "diff: the changed-file set, sorted, repo-relative — the scope basis.",
226
- "items": { "type": "string" }
369
+ "items": {
370
+ "type": "string"
371
+ }
227
372
  },
228
373
  "branch": {
229
374
  "type": "string",
230
375
  "description": "branch: the branch the session was sliced to. With kind \"diff\": the effort metrics (diffCostUsd/Tokens/Turns) were narrowed to this branch's turns (SPEC-0068); absent ⇒ whole-session upper bound."
376
+ },
377
+ "shas": {
378
+ "type": "array",
379
+ "items": {
380
+ "type": "string",
381
+ "pattern": "^[0-9a-f]{40}$"
382
+ },
383
+ "description": "diff: the branch commit SHAs the effort window was anchored on (SPEC-0072 R1; tagless sessions only). Recorded so L1 re-derivation reproduces the window."
231
384
  }
232
385
  }
233
386
  },
@@ -236,10 +389,40 @@
236
389
  "required": ["name", "version", "deterministic", "modelCalls"],
237
390
  "additionalProperties": false,
238
391
  "properties": {
239
- "name": { "type": "string", "minLength": 1 },
240
- "version": { "type": "string", "minLength": 1 },
241
- "deterministic": { "const": true },
242
- "modelCalls": { "const": 0 }
392
+ "name": {
393
+ "type": "string",
394
+ "minLength": 1
395
+ },
396
+ "version": {
397
+ "type": "string",
398
+ "minLength": 1
399
+ },
400
+ "deterministic": {
401
+ "const": true
402
+ },
403
+ "modelCalls": {
404
+ "const": 0
405
+ }
406
+ }
407
+ },
408
+ "prEffort": {
409
+ "type": "object",
410
+ "required": ["priorUsd", "totalUsd", "sessions"],
411
+ "additionalProperties": false,
412
+ "description": "Cumulative PR effort across this branch's successive receipts (SPEC-0072 R3). priorUsd is a recorded input (prior receipt), exempt from L1 byte-compare, envelope-covered.",
413
+ "properties": {
414
+ "priorUsd": {
415
+ "type": "number",
416
+ "minimum": 0
417
+ },
418
+ "totalUsd": {
419
+ "type": "number",
420
+ "minimum": 0
421
+ },
422
+ "sessions": {
423
+ "type": "integer",
424
+ "minimum": 1
425
+ }
243
426
  }
244
427
  }
245
428
  }