altimate-receipts 0.6.2 → 0.8.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/receipt/canonical.ts","../src/report/prComment.ts","../src/report/checks.ts","../src/share/clipboard.ts","../src/share/markdown.ts","../src/sign/envelope.ts","../src/trace/commitMatch.ts","../src/trace/gitCommand.ts","../src/trace/slice.ts","../src/sign/rederive.ts"],"sourcesContent":["/**\n * Canonical JSON (RFC 8785-style): object keys sorted lexicographically, minimal\n * whitespace, arrays in order. Used now for deterministic hashing/equality and\n * reused verbatim as M4's DSSE signing payload, so a Receipt and its signature\n * always agree on the exact bytes.\n *\n * Scope note: this is a pragmatic subset — all numbers in a Receipt are integers\n * or values we round before emitting, so JSON.stringify's number formatting is\n * already stable. We do not implement RFC 8785's full number canonicalization.\n */\nexport function canonicalize(value: unknown): string {\n return serialize(value);\n}\n\nfunction serialize(value: unknown): string {\n if (value === null || typeof value === \"number\" || typeof value === \"boolean\") {\n return JSON.stringify(value);\n }\n if (typeof value === \"string\") {\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) {\n return `[${value.map(serialize).join(\",\")}]`;\n }\n if (typeof value === \"object\") {\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort();\n const body = keys.map((k) => `${JSON.stringify(k)}:${serialize(obj[k])}`).join(\",\");\n return `{${body}}`;\n }\n // undefined / function / symbol — not valid Receipt content\n return \"null\";\n}\n","import { createHash } from \"node:crypto\";\nimport { formatCostAlways, formatTokens } from \"../findings/format.js\";\nimport { destructiveOutsideRepo, findingSurface, privileged } 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/**\n * The **Agent Work Record** PR comment (SPEC-0073/M74) — the de-verdicted renderer.\n * Decided by a 10-persona panel (docs/research/ux-redesign-decision.md): a factual\n * masthead, per-push delta line, transcript-coverage stat, privileged events first,\n * one-line event rows, cleared events struck through with their evidence, an\n * append-only push ledger, and an embedded JSON state block. No grades, no verdicts,\n * no traffic lights anywhere a human reads (R2: evidence, not judgement —\n * `gradeLetter()` survives machine-side only, for opt-in gating).\n *\n * MIRRORED in action/verified-by.mjs — keep the two renderers byte-identical for the\n * same receipt.\n */\n\n// command classes ordered most-notable first, for the session-totals line.\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\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 FEEDBACK_ISSUE_BASE = \"https://github.com/AltimateAI/altimate-receipts/issues/new\";\n\n/** Max Sanity-check rows rendered before the overflow note (SPEC-0073 R5). */\nconst VISIBLE_ROWS = 7;\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; when the receipt's generator is older, a muted\n * upgrade line is appended. The local CLI passes nothing. */\n compareVersion?: string;\n /** Base URL of the PR's files tab (\\`<pr>/files\\`) — when present, file chips and\n * evidence refs link to the file's diff anchor (\\`#diff-sha256(path)R<line>\\`).\n * The Action passes a token action.yml substitutes post-render. */\n diffLinkBase?: string;\n}\n\n/** The merge-surface findings a reviewer sees: merge-gate, non-low severity, and not a\n * destructive op on a scratch path. One predicate shared by renderer + accumulation\n * (cli.ts prCleared) so cleared rows only ever reference events that were shown. */\nexport function visibleMergeFindings(findings: readonly ReceiptFinding[]): ReceiptFinding[] {\n return findings.filter(\n (f) =>\n findingSurface(f.id) === \"merge\" &&\n f.severity !== \"low\" &&\n !(f.id.startsWith(\"destructive-\") && destructiveOutsideRepo(f.title)),\n );\n}\n\n/** Stable event identity across runs/pushes (report/diff.ts discipline). */\nexport function eventKey(f: Pick<ReceiptFinding, \"title\" | \"filePath\">): string {\n return `${f.title} ${f.filePath ?? \"\"}`;\n}\n\n/** Short stable event id for the state block: fnv1a-32 hex over kind-prefix + path. */\nexport function eventId(f: Pick<ReceiptFinding, \"id\" | \"title\" | \"filePath\">): string {\n const prefix = f.id.replace(/-tool-.*$/, \"\").replace(/-\\d+(-\\d+)?$/, \"\");\n let h = 0x811c9dc5;\n for (const ch of `${prefix}:${f.filePath ?? f.title}`) {\n h ^= ch.charCodeAt(0);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h.toString(16).padStart(8, \"0\");\n}\n\n/**\n * Render the Agent Work Record. The Receipt is assumed already redacted.\n */\nexport function renderPrComment(receipt: Receipt, opts: PrCommentOptions = {}): string {\n const p = receipt.predicate;\n const ev = p.evidence;\n const operatorCount = p.findings.filter((f) => findingSurface(f.id) === \"operator\").length;\n const main = visibleMergeFindings(p.findings);\n const cats = categorize(main);\n const files = p.scope?.kind === \"diff\" ? (p.scope.files ?? []) : [];\n\n const priv = main.filter((f) => privileged(f.id, f.filePath));\n const rest = main.filter((f) => !privileged(f.id, f.filePath));\n const cleared = p.prCleared ?? [];\n const lastPush = p.prHistory?.[p.prHistory.length - 1];\n\n const out: string[] = [];\n out.push(PR_COMMENT_MARKER);\n\n // Clean PR (R10): one muted line — never \"Safe\", never silence.\n if (main.length === 0 && cleared.length === 0 && coverageFull(ev, files)) {\n out.push(masthead(p, ev, files));\n out.push(\"\");\n out.push(\n `receipts — nothing detected${coverageCell(ev, files) ? ` · ${coverageCell(ev, files)}` : \"\"}${ev.diffCostUsd != null ? ` · ≈ ${formatCostAlways(ev.diffCostUsd)}` : \"\"} · ${testsCell(ev)}`,\n );\n out.push(\"\");\n out.push(recordDetails(p, ev, cats, operatorCount, opts, feedbackLine(p.session.agent, main)));\n out.push(footer());\n out.push(stateBlock(p, ev, files, main, cleared));\n return `${out.join(\"\\n\")}\\n`;\n }\n\n // Masthead (R2) + delta/coverage line (R3/R4).\n out.push(masthead(p, ev, files));\n out.push(\"\");\n const status = [\n lastPush && p.prHistory && p.prHistory.length > 1\n ? `push ${lastPush.push}${lastPush.sha ? ` · \\`${lastPush.sha}\\`` : \"\"} — +${lastPush.new} new · ${lastPush.cleared} cleared · ${lastPush.open} open`\n : null,\n coverageCell(ev, files),\n ].filter(Boolean);\n if (status.length) {\n out.push(status.join(\" · \"));\n out.push(\"\");\n }\n\n // Tier 1 — privileged surface (R5). Never rendered when empty (NG5).\n if (priv.length) {\n out.push(\"**Review as access control**\");\n for (const f of priv) {\n out.push(eventRow(f, true, files, opts.diffLinkBase));\n }\n out.push(\"\");\n }\n\n // Tier 2 — everything else, capped.\n if (rest.length) {\n out.push(\"**Sanity-check**\");\n for (const f of rest.slice(0, VISIBLE_ROWS)) {\n out.push(eventRow(f, false, files, opts.diffLinkBase));\n }\n if (rest.length > VISIBLE_ROWS) {\n out.push(`- _+${rest.length - VISIBLE_ROWS} more in the record below_`);\n }\n out.push(\"\");\n }\n\n // Cleared events — struck through in place for one push cycle (R6).\n if (cleared.length) {\n for (const c of cleared) {\n const loc = c.filePath ? ` (\\`${c.filePath}\\`)` : \"\";\n out.push(\n `- ~~${c.title}~~${loc} · no longer detected${lastPush ? ` push ${lastPush.push}` : \"\"}`,\n );\n }\n out.push(\"\");\n }\n\n // Negative space — facts about what was NOT detected, with provenance.\n const flaggedSet = new Set(\n main.map((f) => (f.filePath ? displayPath(f.filePath, files) : null)).filter(Boolean),\n );\n if (files.length) {\n out.push(`Other ${Math.max(0, files.length - flaggedSet.size)} files: nothing detected.`);\n out.push(\"\");\n }\n out.push(`<sub>tests: ${testsCell(ev)}</sub>`);\n out.push(\"\");\n\n out.push(recordDetails(p, ev, cats, operatorCount, opts, feedbackLine(p.session.agent, main)));\n out.push(footer());\n out.push(stateBlock(p, ev, files, main, cleared));\n return `${out.join(\"\\n\")}\\n`;\n}\n\n/** `### Agent work record — <agent> · N files[ · k sessions] · spent ≈ $X on this change` */\nfunction masthead(\n p: Receipt[\"predicate\"],\n ev: Receipt[\"predicate\"][\"evidence\"],\n files: readonly string[],\n): string {\n const parts = [`Agent work record — ${p.session.agent}`];\n if (files.length) {\n parts.push(`${files.length} file${files.length === 1 ? \"\" : \"s\"}`);\n }\n // The masthead figure is the PR's total: branch-narrowed cost for a single\n // session (its whole branch work), accumulated prEffort across sessions.\n if (p.prEffort && p.prEffort.sessions > 1) {\n parts.push(`${p.prEffort.sessions} sessions`);\n parts.push(`spent ≈ ${formatCostAlways(p.prEffort.totalUsd)} on this PR`);\n } else if (ev.diffCostUsd != null) {\n parts.push(`spent ≈ ${formatCostAlways(ev.diffCostUsd)} on this PR`);\n }\n return `### ${parts.join(\" · \")}`;\n}\n\n/** `transcript covers N/M changed files` — or the loud gap form (R4). */\nfunction coverageCell(\n ev: Receipt[\"predicate\"][\"evidence\"],\n files: readonly string[],\n): string | null {\n if (ev.coveredFiles == null || !files.length) {\n return null;\n }\n const gap = files.length - ev.coveredFiles;\n if (gap > 0) {\n return `⚠ ${gap} changed file${gap === 1 ? \"\" : \"s\"} ha${gap === 1 ? \"s\" : \"ve\"} no transcript activity (${ev.coveredFiles}/${files.length} covered)`;\n }\n return `transcript covers ${ev.coveredFiles}/${files.length} changed files`;\n}\n\nfunction coverageFull(ev: Receipt[\"predicate\"][\"evidence\"], files: readonly string[]): boolean {\n return ev.coveredFiles == null || !files.length || ev.coveredFiles >= files.length;\n}\n\n/** Render a finding's path repo-relative: match it into the diff's file set when\n * possible (transcript paths are absolute), else keep the last two segments. The\n * absolute worktree path leaked into the first live render — pure noise. */\nexport function displayPath(filePath: string, scopeFiles: readonly string[]): string {\n for (const sf of scopeFiles) {\n if (filePath === sf || filePath.endsWith(`/${sf}`)) {\n return sf;\n }\n }\n if (!filePath.startsWith(\"/\") && !filePath.startsWith(\"~\")) {\n return filePath; // already repo-relative — keep it whole\n }\n const segs = filePath.split(\"/\").filter(Boolean);\n return segs.length > 2 ? segs.slice(-2).join(\"/\") : filePath;\n}\n\n/** GitHub anchors each file in a PR diff as `#diff-<sha256(path)>`; `R<line>` jumps\n * to the right-side line. Pure function of the displayed (repo-relative) path. */\nfunction diffAnchor(base: string, path: string, line?: number): string {\n const h = createHash(\"sha256\").update(path).digest(\"hex\");\n return `${base}#diff-${h}${line ? `R${line}` : \"\"}`;\n}\n\n/** One event row: `file` — **fact** · why it surfaced · evidence link (R5). */\nfunction eventRow(\n f: ReceiptFinding,\n priv: boolean,\n scopeFiles: readonly string[],\n linkBase?: string,\n): string {\n const mark = priv ? \"▲ \" : \"\";\n const shown = f.filePath ? displayPath(f.filePath, scopeFiles) : undefined;\n const chip = shown ? `\\`${shown}${f.line ? `:${f.line}` : \"\"}\\`` : \"\";\n const file = chip\n ? `${linkBase && shown ? `[${chip}](${diffAnchor(linkBase, shown, f.line)})` : chip} — `\n : \"\";\n // Detector convention titles end \": <basename>\" — strip ONLY that exact shape so a\n // fact never loses a mid-sentence filename (\"…swallowed an error in cli.ts\" stays).\n const base = f.filePath?.split(\"/\").pop();\n let fact = f.title;\n if (base) {\n fact = fact.replace(new RegExp(`:\\\\s*\\`?${escapeRe(base)}\\`?\\\\s*$`), \"\").trim();\n }\n const why = f.impactLabel ? ` · ${f.impactLabel}` : \"\";\n // The evidence ref links to the same diff spot (the transcript itself is local —\n // the runnable \\`receipts log --ref\\` command lives in the Record below).\n const ref = f.evidenceRef\n ? linkBase && shown\n ? ` · [evidence \\`${f.evidenceRef}\\`](${diffAnchor(linkBase, shown, f.line)})`\n : ` · evidence \\`${f.evidenceRef}\\``\n : \"\";\n return `- ${mark}${file}**${fact}**${why}${ref}`;\n}\n\nfunction escapeRe(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Tests cell — parsed counts with provenance, or honest ignorance. Never \"✅ ran\". */\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 return `${seg || tm.exitStatus} _(parsed from runner output in transcript)_`;\n }\n return ev.testsRan\n ? \"test command observed — outcome not parsed\"\n : \"no test run detected in transcript\";\n}\n\n/** The collapsed Record — ledger, checks (\"not detected\"), files, sessions, custody. */\nfunction recordDetails(\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\n // Append-only push ledger (R7).\n if (p.prHistory?.length) {\n inner.push(\"| push | events | change cost |\", \"| :-- | :-- | :-- |\");\n for (const h of p.prHistory) {\n const id = h.sha ? ` · \\`${h.sha}\\`` : \"\";\n const cost = h.costUsd != null ? formatCostAlways(h.costUsd) : \"—\";\n inner.push(\n `| ${h.push}${id}${dateCell(h.endedAt)} | +${h.new} new · ${h.cleared} cleared · ${h.open} open | ${cost} |`,\n );\n }\n inner.push(\n \"<sub>change cost per row = the attributed cost at that push (cumulative within a session).</sub>\",\n \"\",\n );\n }\n\n const files = p.scope?.kind === \"diff\" ? (p.scope.files ?? []) : [];\n if (files.length) {\n inner.push(changedFilesBlock(files), \"\");\n }\n const ledger = renderLedger(ev.claims ?? []);\n if (ledger) {\n inner.push(ledger, \"\");\n }\n\n // Check readings — lab-report register: \"not detected\", never \"clear\"/\"✅\".\n inner.push(\n `**Checks** _(\"not detected\" means this check found nothing — not that nothing exists)_`,\n \"\",\n checksTable(cats),\n \"\",\n );\n\n const cl = diffCostLine(ev, p.prEffort);\n if (cl) {\n inner.push(cl);\n }\n inner.push(`<sub>${effortLine(ev, operatorCount)}</sub>`);\n\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>\\`) · pull any event's tape: \\`npx altimate-receipts log --ref <evidence-id>\\` · 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) {\n inner.push(hint);\n }\n inner.push(feedback);\n return [\n \"<details><summary>Record — pushes, files, checks, custody (append-only)</summary>\",\n \"\",\n ...inner,\n \"\",\n \"</details>\",\n ].join(\"\\n\");\n}\n\nfunction dateCell(endedAt?: number): string {\n if (!endedAt || !Number.isFinite(endedAt)) {\n return \"\";\n }\n return ` · ${new Date(endedAt).toISOString().slice(0, 10)}`;\n}\n\nfunction footer(): string {\n return `\\n<sub>[what is this? ›](${ABOUT_DOC_URL}) — a record of the agent's process, not a code review · entries are never edited or removed</sub>`;\n}\n\n/** Embedded versioned state block (R8) — machine mirror for fleets/rollups. Key-sorted\n * for cross-renderer byte parity. */\nfunction stateBlock(\n p: Receipt[\"predicate\"],\n ev: Receipt[\"predicate\"][\"evidence\"],\n files: readonly string[],\n main: readonly ReceiptFinding[],\n cleared: readonly { title: string; filePath?: string }[],\n): string {\n const lastPush = p.prHistory?.[p.prHistory.length - 1]?.push ?? 1;\n const events = [\n ...main.map((f) => ({\n file: f.filePath ? displayPath(f.filePath, files) : \"\",\n id: eventId({ ...f, filePath: f.filePath ? displayPath(f.filePath, files) : undefined }),\n privileged: privileged(f.id, f.filePath),\n state: \"open\",\n type: f.id.replace(/-tool-.*$/, \"\").replace(/-\\d+(-\\d+)?$/, \"\"),\n })),\n ...cleared.map((c) => ({\n file: c.filePath ? displayPath(c.filePath, files) : \"\",\n id: eventId({\n id: \"cleared\",\n title: c.title,\n filePath: c.filePath ? displayPath(c.filePath, files) : undefined,\n }),\n privileged: false,\n state: \"cleared\",\n type: \"cleared\",\n })),\n ];\n const state = {\n cost_cents:\n ev.diffCostUsd != null ? Math.round((p.prEffort?.totalUsd ?? ev.diffCostUsd) * 100) : null,\n coverage: ev.coveredFiles != null ? { covered: ev.coveredFiles, total: files.length } : null,\n events,\n push: lastPush,\n schema_version: 1,\n };\n return `<!-- receipts-state v1 ${JSON.stringify(state)} -->`;\n}\n\n/** Changed-file block — grouped by dir for big PRs (unchanged from the old renderer). */\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).\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 an older CLI than the verifier. */\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/** Feedback footer — prefilled false-positive issue (no grade — events only). */\nfunction feedbackLine(agent: string, flagged: readonly ReceiptFinding[]): 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}`,\n \"\",\n \"**Flagged event(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 or noisy event? [Report it — prefilled ›](${url}) · or react 👍/👎 on this comment. It tunes the checks.</sub>`;\n}\n\n/** Check readings table — \"N detected\" / \"not detected\" (lab-report register). */\nfunction checksTable(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 ? `${fl.count} detected` : \"not detected\";\n return `| ${c.label} | ${result} |`;\n });\n rows.sort((a, b) => (a.includes(\"not detected\") ? 1 : 0) - (b.includes(\"not detected\") ? 1 : 0));\n return [\"| check | reading |\", \"| :-- | :-- |\", ...rows].join(\"\\n\");\n}\n\n/** The diff-cost line — this change's cost, an upper bound; never the session total. */\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 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/** 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 (may span other work): ${ev.edits} edits · ${ev.commands} commands${brk} · ${formatCostAlways(ev.costUsd)} · ${formatTokens(ev.tokens.total)} tokens${op}.`;\n}\n","/**\n * The catalog of merge-surface checks the engine runs on every diff. The PR comment\n * lists it so a reviewer sees *what was mechanically scanned for* — the cleared checks\n * are the trust surface (\"a deterministic auditor looked for these and found nothing\"),\n * the flagged ones point at the Findings above. This is a render-time partition of the\n * existing finding ids — it adds no detector and makes no judgement: \"cleared\" means\n * **no detector fired on this diff**, never \"this code is good\" (SPEC-0000 R2).\n *\n * Keep this in lock-step with the detectors: every merge-surface finding id prefix MUST\n * map to a category here (the `categorize` self-check throws in dev if one doesn't), so\n * the catalog never silently under-claims or claims a check that doesn't run.\n */\n\nexport interface Check {\n key: string;\n icon: string;\n label: string;\n /** matches the finding-id prefixes this category covers */\n prefixes: string[];\n}\n\n/** Ordered by review priority. Operator/efficiency findings (loop, errcluster, …) are\n * deliberately absent — they live on the `receipts` card, not the merge gate. */\nexport const CHECK_CATALOG: readonly Check[] = [\n {\n key: \"destructive\",\n icon: \"🛡️\",\n label: \"destructive ops\",\n prefixes: [\"destructive\", \"file-shrink\"],\n },\n {\n key: \"git-history\",\n icon: \"🌿\",\n label: \"git-history safety\",\n prefixes: [\"force-push\", \"history-rewrite\"],\n },\n { key: \"secrets\", icon: \"🔑\", label: \"secret leaks\", prefixes: [\"secret\"] },\n { key: \"cicd\", icon: \"⚙️\", label: \"CI/CD tampering\", prefixes: [\"ci-cd-touch\"] },\n { key: \"lockfile\", icon: \"📦\", label: \"lockfile integrity\", prefixes: [\"lockfile-edit\"] },\n {\n key: \"tests\",\n icon: \"🧪\",\n label: \"test integrity\",\n prefixes: [\n \"fake-green\",\n \"test-focus\",\n \"test-skipped\",\n \"test-trivialised\",\n \"grader-edit\",\n \"eval-override\",\n ],\n },\n {\n key: \"weakening\",\n icon: \"🔓\",\n label: \"config/safety weakening\",\n prefixes: [\"config-weaken\", \"hook-bypass\", \"pipe-sh\"],\n },\n {\n key: \"blind-edits\",\n icon: \"👁️\",\n label: \"blind / unverified edits\",\n prefixes: [\"blind-edit\", \"unverified-change\", \"edit-reversion\"],\n },\n { key: \"injection\", icon: \"💉\", label: \"prompt-injection\", prefixes: [\"prompt-injection\"] },\n { key: \"trojan\", icon: \"🕵️\", label: \"hidden unicode\", prefixes: [\"trojan-source\"] },\n {\n key: \"duplication\",\n icon: \"📋\",\n label: \"duplicated code\",\n prefixes: [\"dup-file\", \"duplicated-code\"],\n },\n { key: \"swallowed\", icon: \"🤫\", label: \"swallowed errors\", prefixes: [\"error-swallowed\"] },\n { key: \"stubbed\", icon: \"🚧\", label: \"stubbed implementation\", prefixes: [\"impl-stubbed\"] },\n { key: \"malformed\", icon: \"🧱\", label: \"malformed artifacts\", prefixes: [\"malformed-artifact\"] },\n { key: \"truncated\", icon: \"✂️\", label: \"truncated turns\", prefixes: [\"truncated-turn\"] },\n {\n key: \"promises\",\n icon: \"🤝\",\n label: \"unfulfilled claims\",\n prefixes: [\"claimed-commit-none\", \"unfulfilled-promise\"],\n },\n {\n key: \"tool-calls\",\n icon: \"🧬\",\n label: \"tool-call integrity\",\n prefixes: [\"tool-fabricated\", \"tool-malformed-args\"],\n },\n] as const;\n\n/** A finding id matches a category if it equals or `<prefix>-`-starts one of its prefixes. */\nfunction matches(id: string, check: Check): boolean {\n return check.prefixes.some((p) => id === p || id.startsWith(`${p}-`));\n}\n\n/** The catalog check a finding id belongs to (for cross-receipt rollups), or undefined. */\nexport function checkForId(id: string): Check | undefined {\n return CHECK_CATALOG.find((c) => matches(id, c));\n}\n\nexport type CheckSeverity = \"critical\" | \"high\" | \"medium\" | \"low\";\nconst SEV_RANK: Record<CheckSeverity, number> = { critical: 0, high: 1, medium: 2, low: 3 };\n\nexport interface FlaggedCheck {\n check: Check;\n count: number;\n /** worst severity among the findings that matched this category */\n severity: CheckSeverity;\n}\n\nexport interface CategorizedChecks {\n /** categories with ≥1 merge finding on this diff, each with count + worst severity */\n flagged: FlaggedCheck[];\n /** categories that ran and found nothing — the trust surface */\n cleared: Check[];\n /** total categories in the catalog */\n total: number;\n}\n\n/**\n * Partition the catalog against a set of (merge-surface) findings. A category is\n * `flagged` when ≥1 finding matches it (carrying the count + worst severity), else\n * `cleared`. The order of {@link CHECK_CATALOG} is preserved in both lists.\n */\nexport function categorize(\n findings: readonly { id: string; severity?: CheckSeverity }[],\n): CategorizedChecks {\n const flagged: FlaggedCheck[] = [];\n const cleared: Check[] = [];\n for (const check of CHECK_CATALOG) {\n const hits = findings.filter((f) => matches(f.id, check));\n if (hits.length > 0) {\n const severity = hits\n .map((f) => f.severity ?? \"high\")\n .sort((a, b) => SEV_RANK[a] - SEV_RANK[b])[0];\n flagged.push({ check, count: hits.length, severity });\n } else {\n cleared.push(check);\n }\n }\n return { flagged, cleared, total: CHECK_CATALOG.length };\n}\n","import { spawnSync } from \"node:child_process\";\n\ninterface ClipboardTool {\n cmd: string;\n args: string[];\n}\n\n/** OS clipboard tools, by platform, in preference order. */\nfunction candidates(): ClipboardTool[] {\n if (process.platform === \"darwin\") {\n return [{ cmd: \"pbcopy\", args: [] }];\n }\n if (process.platform === \"win32\") {\n return [{ cmd: \"clip\", args: [] }];\n }\n // linux / other: Wayland first, then X11\n return [\n { cmd: \"wl-copy\", args: [] },\n { cmd: \"xclip\", args: [\"-selection\", \"clipboard\"] },\n { cmd: \"xsel\", args: [\"--clipboard\", \"--input\"] },\n ];\n}\n\n/**\n * Best-effort copy to the OS clipboard. Returns true on success, false if no tool\n * is available or the copy failed — the caller prints a note and still exits 0.\n * No npm dependency; shells out to the platform tool.\n */\nexport function copyToClipboard(text: string): boolean {\n for (const { cmd, args } of candidates()) {\n try {\n const res = spawnSync(cmd, args, { input: text, stdio: [\"pipe\", \"ignore\", \"ignore\"] });\n if (!res.error && res.status === 0) {\n return true;\n }\n } catch {\n // try the next tool\n }\n }\n return false;\n}\n","import type { FindingSet, Severity } from \"../findings/findings.js\";\nimport { type GradeLetter, gradeLetter } from \"../findings/grade.js\";\nimport type { DerivedSummary } from \"../findings/spans.js\";\nimport { deriveEvidence } from \"../receipt/build.js\";\nimport type { Session, SessionSummary } from \"../trace/types.js\";\nimport { redact } from \"./redact.js\";\n\nconst ICON: Record<Severity, string> = {\n critical: \"⛔\",\n high: \"⚠️\",\n medium: \"🔍\",\n low: \"·\",\n};\n\nconst base = (p: string): string => p.split(\"/\").pop() ?? p;\n\n/**\n * A redacted, paste-ready Markdown summary of a session — the `--share` artifact.\n * Pure function of the derived data; all user-text is passed through `redact()`.\n */\nexport function renderShareMarkdown(args: {\n summary: SessionSummary;\n session: Session;\n derived: DerivedSummary;\n findings: FindingSet;\n}): string {\n const { summary, session, derived, findings } = args;\n const { main, minor } = findings;\n const g = gradeLetter(main);\n const ev = deriveEvidence(session, derived);\n\n const counts =\n ([\"critical\", \"high\", \"medium\"] as const)\n .map((s) => {\n const n = main.filter((f) => f.severity === s).length;\n return n ? `${n} ${s}` : null;\n })\n .filter(Boolean)\n .join(\" · \") || \"no findings\";\n\n const title = redact(summary.title || \"untitled session\");\n const out: string[] = [];\n out.push(`## 🧾 Receipt — ${title} · Grade ${g}`);\n out.push(\"\");\n out.push(\n `**${main.length ? `${main.length} EVENT${main.length === 1 ? \"\" : \"S\"} ON RECORD` : \"NOTHING DETECTED\"}** · ${counts} · _${summary.source}_`,\n );\n out.push(\"\");\n\n if (main.length) {\n out.push(\"### Findings\");\n const order: Record<Severity, number> = { critical: 0, high: 1, medium: 2, low: 3 };\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 const tag = f.impactLabel ? ` — ${redact(f.impactLabel)}` : \"\";\n const loc = f.filePath ? ` (\\`${redact(base(f.filePath))}\\`)` : \"\";\n out.push(`- ${ICON[f.severity]} **${redact(f.title)}**${tag}${loc}`);\n }\n if (minor.length) {\n out.push(`- _+${minor.length} minor (collapsed)_`);\n }\n out.push(\"\");\n }\n\n out.push(\"### Evidence\");\n out.push(\n `${ev.filesChanged} files · ${ev.edits} edits · ${ev.commands} commands · ` +\n `${ev.testsRan ? \"tests ran ✓\" : \"no test run detected\"} · ` +\n `${ev.destructiveOps} destructive ops`,\n );\n out.push(\"\");\n out.push(\"_Verified by Receipts · deterministic · 0 model calls · evidence, not judgement_\");\n return `${out.join(\"\\n\")}\\n`;\n}\n","import type { Receipt } from \"../receipt/build.js\";\nimport { canonicalize } from \"../receipt/canonical.js\";\n\n/** in-toto payloads ride in DSSE with this type. */\nexport const DSSE_PAYLOAD_TYPE = \"application/vnd.in-toto+json\";\n\nexport interface DsseSignature {\n sig: string;\n keyid?: string;\n}\n\nexport interface DsseEnvelope {\n payloadType: string;\n /** base64 of the canonical Receipt JSON */\n payload: string;\n signatures: DsseSignature[];\n}\n\n/**\n * DSSE Pre-Authentication Encoding (PAE) — the exact bytes a signer signs:\n * \"DSSEv1\" SP len(payloadType) SP payloadType SP len(payload) SP payload\n * Lengths count UTF-8 bytes; `payload` here is the raw (un-base64) payload.\n */\nexport function pae(payloadType: string, payload: string): Buffer {\n const typeBytes = Buffer.from(payloadType, \"utf8\");\n const bodyBytes = Buffer.from(payload, \"utf8\");\n const header = `DSSEv1 ${typeBytes.length} ${payloadType} ${bodyBytes.length} `;\n return Buffer.concat([Buffer.from(header, \"utf8\"), bodyBytes]);\n}\n\n/**\n * Wrap a Receipt as an UNSIGNED DSSE envelope. The payload is the canonical\n * Receipt JSON (so signature and content always agree on the exact bytes). The\n * signature array is filled by the signer (the GitHub Action), not here.\n */\nexport function toDsseEnvelope(receipt: Receipt): DsseEnvelope {\n const canonical = canonicalize(receipt);\n return {\n payloadType: DSSE_PAYLOAD_TYPE,\n payload: Buffer.from(canonical, \"utf8\").toString(\"base64\"),\n signatures: [],\n };\n}\n\n/** The PAE bytes for an envelope (what a verifier checks the signature against). */\nexport function envelopePae(env: DsseEnvelope): Buffer {\n const raw = Buffer.from(env.payload, \"base64\").toString(\"utf8\");\n return pae(env.payloadType, raw);\n}\n","import { spawnSync } from \"node:child_process\";\nimport type { DerivedSpan } from \"../findings/spans.js\";\nimport { gitInvocations } from \"./gitCommand.js\";\n\n/**\n * Commit-SHA attribution (SPEC-0069 / M70). When any agent runs `git commit` or\n * `git push`, the captured output carries the commit SHA (\"[feat-x 5f8a31d] msg\",\n * \"abc1234..def5678 feat-x -> feat-x\"). Matching those SHAs against the branch's\n * `git log` gives an exact, agent-agnostic session↔branch binding — no branch\n * tags, no edit spans, nothing new recorded. Prior art: aider stamps commits,\n * git-ai/Entire attach notes; we read the binding the transcript already holds.\n */\n\n/** Max branch commits to match against — far beyond any sane PR; newest first. */\nconst SHA_CAP = 200;\n\n/** Full SHAs of the commits unique to this branch (newest first), or [] when\n * unknowable (no base / no commits / no git). */\nexport function branchShas(base?: string, cwd?: string): string[] {\n if (!base) {\n return [];\n }\n const r = spawnSync(\"git\", [\"log\", `--max-count=${SHA_CAP}`, \"--format=%H\", `${base}..HEAD`], {\n encoding: \"utf8\",\n cwd,\n });\n if (r.status !== 0) {\n return [];\n }\n return r.stdout.split(\"\\n\").filter((s) => /^[0-9a-f]{40}$/.test(s));\n}\n\nconst HEX_RUN = /\\b[0-9a-f]{7,40}\\b/g;\n\n/** The command string of a tool span, across agent input shapes: Claude's\n * `{command}` object, Codex's raw arguments JSON string (`'{\"cmd\":\"…\"}'`, string\n * or argv array inside). Raw inputs per R5. */\nfunction commandOf(input: unknown): string {\n if (typeof input === \"string\") {\n const t = input.trim();\n if (t.startsWith(\"{\")) {\n try {\n return commandOf(JSON.parse(t));\n } catch {\n return input;\n }\n }\n return input;\n }\n if (input && typeof input === \"object\") {\n const o = input as Record<string, unknown>;\n for (const key of [\"command\", \"cmd\"]) {\n const v = o[key];\n if (typeof v === \"string\") {\n return v;\n }\n if (Array.isArray(v)) {\n return v.filter((t) => typeof t === \"string\").join(\" \");\n }\n }\n }\n return \"\";\n}\n\n/**\n * Is this command a real `git commit`/`git push` invocation? Tokenized via the\n * shared M67-discipline matcher — an orchestrating session running\n * `codex exec \"...then git push...\"` (the SHA-bearing instruction-in-a-string\n * case, observed live) never counts as the author of its child agent's commits.\n */\nexport function isGitWrite(command: string): boolean {\n return gitInvocations(command).some((g) => g.sub === \"commit\" || g.sub === \"push\");\n}\n\n/**\n * Did this session author any of the branch's commits? True iff a hex run (≥7\n * chars) in the OUTPUT of a `git commit`/`git push` command span prefixes one of\n * the branch's full SHAs. Output-only: a SHA pasted into a command's input isn't\n * authorship. Deterministic string scan; zero git calls.\n */\nexport function authoredBranch(spans: readonly DerivedSpan[], shas: readonly string[]): boolean {\n if (shas.length === 0) {\n return false;\n }\n for (const s of spans) {\n if (s.kind !== \"tool\" || typeof s.output !== \"string\") {\n continue;\n }\n if (!isGitWrite(commandOf(s.input))) {\n continue;\n }\n for (const token of s.output.match(HEX_RUN) ?? []) {\n if (shas.some((full) => full.startsWith(token))) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * The branch's commit WINDOW inside a session (SPEC-0072 R1): span indices from\n * just after the last FOREIGN commit anchor preceding the branch's first own\n * anchor, through the last own anchor. Own anchor = a `git commit`/`git push`\n * span whose OUTPUT contains one of the branch's SHAs (same output-only\n * discipline as `authoredBranch`); foreign anchor = such a span whose output\n * carries hex runs but none of ours — the previous sibling branch's commit in a\n * multi-PR session. Null when the session authored none of the branch's commits.\n * Pure span scan ⇒ re-derivable at verify from the transcript + recorded SHAs.\n */\nexport function shaWindow(\n spans: readonly DerivedSpan[],\n shas: readonly string[],\n): { start: number; end: number } | null {\n if (shas.length === 0) {\n return null;\n }\n let firstOwn = -1;\n let lastOwn = -1;\n let lastForeignBeforeOwn = -1;\n spans.forEach((s, i) => {\n if (s.kind !== \"tool\" || typeof s.output !== \"string\") {\n return;\n }\n if (!isGitWrite(commandOf(s.input))) {\n return;\n }\n const tokens = s.output.match(HEX_RUN) ?? [];\n if (tokens.length === 0) {\n return;\n }\n const own = tokens.some((t) => shas.some((full) => full.startsWith(t)));\n if (own) {\n if (firstOwn === -1) {\n firstOwn = i;\n }\n lastOwn = i;\n } else if (firstOwn === -1) {\n lastForeignBeforeOwn = i;\n }\n });\n if (lastOwn === -1) {\n return null;\n }\n return { start: lastForeignBeforeOwn + 1, end: lastOwn };\n}\n","/**\n * Shared git-invocation tokenizer (extracted in SPEC-0069 — the M67 push matcher\n * and the M70 authorship matcher need identical discipline). Quoted text is\n * blanked first, so `git push` inside a prompt string (`codex exec \"...git\n * push...\"`, `echo \"git push\"`, commit-message text) never reads as a git call;\n * then each `&&`/`||`/`;`/`|`-separated simple command must START with `git`\n * (after env assignments / benign wrappers and git's global flags).\n */\n\n/** Shell wrappers that may legitimately precede `git` in a simple command. */\nconst WRAPPERS = new Set([\"command\", \"exec\", \"nohup\", \"time\", \"env\"]);\n/** Global git flags between `git` and the subcommand that consume a value. */\nconst GIT_VALUE_FLAGS = new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--exec-path\"]);\n\nexport interface GitInvocation {\n /** the git subcommand: \"push\", \"commit\", \"log\", … */\n sub: string;\n /** tokens after the subcommand (flags + positionals) */\n rest: string[];\n}\n\n/** Every real `git <sub> …` invocation in a (possibly compound) shell command. */\nexport function gitInvocations(command: string): GitInvocation[] {\n const blanked = command\n .replace(/\\\\[\"']/g, \" \")\n .replace(/'[^']*'/g, \" \")\n .replace(/\"[^\"]*\"/g, \" \");\n const out: GitInvocation[] = [];\n for (const simple of blanked.split(/(?:&&|\\|\\||[;|\\n])/)) {\n const tokens = simple.trim().split(/\\s+/).filter(Boolean);\n let i = 0;\n while (\n i < tokens.length &&\n (/^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[i]) || WRAPPERS.has(tokens[i]))\n ) {\n i++;\n }\n if (tokens[i] !== \"git\") {\n continue;\n }\n i++;\n while (i < tokens.length && tokens[i].startsWith(\"-\")) {\n const flag = tokens[i];\n i++;\n if (GIT_VALUE_FLAGS.has(flag)) {\n i++;\n }\n }\n const sub = tokens[i];\n if (sub) {\n out.push({ sub, rest: tokens.slice(i + 1) });\n }\n }\n return out;\n}\n","import type { Session, SessionMessage } from \"./types.js\";\n\n/** The distinct branches that appear across a session's messages, in first-seen order. */\nexport function branchesIn(session: Session): string[] {\n const seen: string[] = [];\n for (const m of session.messages) {\n if (m.gitBranch && !seen.includes(m.gitBranch)) {\n seen.push(m.gitBranch);\n }\n }\n return seen;\n}\n\n/**\n * Assign every message an effective branch: its own `gitBranch`, else the nearest\n * preceding tagged branch (carry-forward); leading untagged messages take the\n * first branch that appears. Deterministic.\n */\nfunction effectiveBranches(messages: SessionMessage[]): (string | undefined)[] {\n const firstTagged = messages.find((m) => m.gitBranch)?.gitBranch;\n let current = firstTagged;\n return messages.map((m) => {\n if (m.gitBranch) {\n current = m.gitBranch;\n }\n return current;\n });\n}\n\n/**\n * Slice a session down to the messages belonging to `branch` (non-contiguous\n * visits included), recomputing the per-session `digestSource` over the slice so\n * the Receipt's subject digest is scoped to this branch's work. Deterministic.\n *\n * Returns null when the branch isn't present in the session at all.\n */\nexport function sliceByBranch(session: Session, branch: string): Session | null {\n if (!branchesIn(session).includes(branch)) {\n return null;\n }\n const eff = effectiveBranches(session.messages);\n const messages = session.messages.filter((_, i) => eff[i] === branch);\n if (messages.length === 0) {\n return null;\n }\n const timestamps = messages\n .map((m) => m.timestamp)\n .filter((t): t is number => typeof t === \"number\");\n const startedAt = timestamps.length ? Math.min(...timestamps) : session.startedAt;\n const endedAt = timestamps.length ? Math.max(...timestamps) : session.endedAt;\n\n return {\n ...session,\n id: `${session.id}#${branch}`,\n gitBranch: branch,\n startedAt,\n endedAt,\n messages,\n // Scope the subject digest to this branch's slice (reproducible by re-slicing).\n digestSource: JSON.stringify({ id: session.id, branch, messages }),\n };\n}\n","import { deriveFindings } from \"../findings/findings.js\";\nimport { deriveSpans } from \"../findings/spans.js\";\nimport { type Receipt, type ReceiptScope, buildReceipt } from \"../receipt/build.js\";\nimport { canonicalize } from \"../receipt/canonical.js\";\nimport { redactReceipt } from \"../share/redact.js\";\nimport { shaWindow } from \"../trace/commitMatch.js\";\nimport { applyDiffScope, narrowEffort, windowedEffort } from \"../trace/diffScope.js\";\nimport { loadById } from \"../trace/load.js\";\nimport { agentIds } from \"../trace/registry.js\";\nimport { sliceByBranch } from \"../trace/slice.js\";\nimport type { AgentSource } from \"../trace/types.js\";\n\n/**\n * The agent that produced a receipt, read from its subject prefix (`<source>/<id>`,\n * set in buildReceipt). Re-derivation must load the transcript through the SAME\n * adapter the receipt was built with, or the bytes can't match (e.g. a `codex/…`\n * receipt re-derived through the Claude adapter yields empty evidence). Returns\n * undefined for an unknown prefix so the caller keeps its default.\n */\nexport function sourceFromReceipt(receipt: Receipt): AgentSource | undefined {\n const prefix = receipt.subject?.[0]?.name?.split(\"/\")[0];\n return agentIds().find((id) => id === prefix);\n}\n\nexport interface RederiveOptions {\n /** agent that produced the transcript (default: claude-code) */\n source?: AgentSource;\n /** scope to one branch's turns before deriving */\n branch?: string;\n /** the committed receipt's recorded scope — re-applied so the re-derivation\n * matches a scoped receipt byte-for-byte (SPEC-0013 L1). Its `files`/`branch`\n * come from the receipt, so no git is needed at verify time. */\n scope?: ReceiptScope;\n /** apply secret redaction (to compare against a redacted committed Receipt) */\n redact?: boolean;\n}\n\n/**\n * Re-run the deterministic engine on a transcript file to reproduce its Receipt.\n * Because the pipeline is deterministic, a verifier with the transcript gets the\n * exact same Receipt — that's what proves a committed Receipt is faithful and not\n * fabricated (SPEC-0009 Part B, L1). When the committed receipt was scoped\n * (SPEC-0013), the same recorded scope is re-applied so the bytes still match.\n */\nexport async function rederiveFromTranscript(\n path: string,\n opts: RederiveOptions = {},\n): Promise<Receipt | null> {\n const loaded = await loadById(opts.source ?? \"claude-code\", path);\n if (!loaded) {\n return null;\n }\n // Resolve scope: the recorded `predicate.scope` takes precedence; fall back to a\n // bare `--branch` for pre-SPEC-0013 (branch-only) receipts.\n const scope = opts.scope;\n const branch = scope?.kind === \"branch\" ? scope.branch : opts.branch;\n const session = branch ? (sliceByBranch(loaded, branch) ?? loaded) : loaded;\n\n let derived = deriveSpans(session);\n let findings = deriveFindings(derived);\n if (scope?.kind === \"diff\" && scope.files) {\n const scoped = applyDiffScope(derived, findings, scope.files, session.projectPath);\n derived = scoped.derived;\n findings = scoped.findings;\n if (scope.branch) {\n // SPEC-0068 R4: the receipt declares branch-narrowed effort — recompute it the\n // same way (branch turns ∩ diff-file edits) so the byte-compare holds.\n const slice = sliceByBranch(loaded, scope.branch);\n let eff = narrowEffort(slice ? deriveSpans(slice) : null, scope.files);\n if (!eff && scope.shas?.length) {\n // SPEC-0072 R2: a tagless session declared a SHA-anchored commit window —\n // reproduce it from the transcript + the recorded anchors.\n const win = shaWindow(derived.spans, scope.shas);\n if (win) {\n eff = windowedEffort(derived, scope.files, win);\n }\n }\n if (eff) {\n derived = {\n ...derived,\n diffCostUsd: eff.cost,\n diffTokens: eff.tokens,\n diffTurns: eff.turns,\n };\n }\n }\n }\n\n const receipt = await buildReceipt(session, derived, findings, { scope });\n return opts.redact ? redactReceipt(receipt) : receipt;\n}\n\nexport interface RederiveCompare {\n /** the re-derivation produced a Receipt at all */\n rederived: boolean;\n /** the committed Receipt byte-equals the re-derivation (faithful, not fabricated) */\n matches: boolean;\n}\n\n/**\n * Compare a committed Receipt against a fresh re-derivation from its transcript.\n * `matches` true ⇒ the Receipt is the deterministic function of that transcript.\n */\nexport async function compareToTranscript(\n committed: Receipt,\n transcriptPath: string,\n opts: RederiveOptions = {},\n): Promise<RederiveCompare> {\n // Re-apply the committed receipt's own recorded scope so a scoped receipt\n // re-derives byte-for-byte (the basis is in the receipt, so no git is needed).\n const fresh = await rederiveFromTranscript(transcriptPath, {\n redact: true,\n ...opts,\n // Re-derive through the adapter the receipt names (its subject prefix), so a\n // codex/cursor receipt isn't re-run through the Claude adapter. An explicit\n // opts.source still wins.\n source: opts.source ?? sourceFromReceipt(committed),\n scope: committed.predicate.scope,\n });\n if (!fresh) {\n return { rederived: false, matches: false };\n }\n // SPEC-0072 R4: `prEffort` accumulates PRIOR receipts — by construction not a\n // function of this transcript, so it is exempt from the byte-compare. It stays\n // tamper-evident through the envelope/attestation digest.\n const strip = (r: Receipt): Receipt => {\n if (!r.predicate.prEffort && !r.predicate.prHistory && !r.predicate.prCleared) {\n return r;\n }\n const { prEffort: _x, prHistory: _y, prCleared: _z, ...rest } = r.predicate;\n return { ...r, predicate: rest as Receipt[\"predicate\"] };\n };\n return {\n rederived: true,\n matches: canonicalize(strip(committed)) === canonicalize(strip(fresh)),\n };\n}\n"],"mappings":"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","names":["base","spawnSync","base","spawnSync"]}
@@ -2746,6 +2746,296 @@ function gradeLetter(main) {
2746
2746
  return "A";
2747
2747
  }
2748
2748
 
2749
+ // src/trace/diffScope.ts
2750
+ import { spawnSync } from "child_process";
2751
+
2752
+ // src/findings/surface.ts
2753
+ var OPERATOR_KINDS = /* @__PURE__ */ new Set([
2754
+ "loop",
2755
+ "bottleneck",
2756
+ "cost-concentration",
2757
+ "cache-opportunity",
2758
+ "model-downgrade"
2759
+ ]);
2760
+ var PRIVILEGED_PREFIXES = [
2761
+ "ci-cd-touch",
2762
+ "lockfile-edit",
2763
+ "hook-bypass",
2764
+ "config-weaken",
2765
+ "grader-edit",
2766
+ "eval-override",
2767
+ "test-focus",
2768
+ "test-skipped",
2769
+ "test-trivialised",
2770
+ "history-rewrite",
2771
+ "force-push"
2772
+ ];
2773
+ var TEST_PATH2 = /(?:^|\/)(?:tests?|specs?|__tests__)(?:\/|$)|\.(?:test|spec)\.|_test\./;
2774
+ function privileged(id, filePath) {
2775
+ if (PRIVILEGED_PREFIXES.some((p) => id.startsWith(p))) {
2776
+ return true;
2777
+ }
2778
+ return id.startsWith("file-shrink") && !!filePath && TEST_PATH2.test(filePath);
2779
+ }
2780
+ function findingSurface(id) {
2781
+ if (OPERATOR_KINDS.has(id) || id.startsWith("errcluster-")) {
2782
+ return "operator";
2783
+ }
2784
+ return "merge";
2785
+ }
2786
+ function destructiveOutsideRepo(title) {
2787
+ const clause = title.replace(/^Destructive op:\s*/i, "").replace(/\s*×\d+\s*$/, "");
2788
+ const m = clause.match(/^\s*(rm|rmdir)\b(.*)$/i);
2789
+ if (!m) {
2790
+ return false;
2791
+ }
2792
+ const operands = m[2].split(/\s+/).filter((t) => t && !t.startsWith("-"));
2793
+ if (operands.length === 0) {
2794
+ return false;
2795
+ }
2796
+ const scratch = (t) => {
2797
+ const p = t.replace(/^["']|["']$/g, "");
2798
+ return /\$/.test(p) || p.startsWith("/tmp") || p.startsWith("/var/folders") || p.startsWith("/private/var/folders") || p.startsWith("~");
2799
+ };
2800
+ return operands.every(scratch);
2801
+ }
2802
+ function regenerableReceiptRm(title) {
2803
+ const clause = title.replace(/^Destructive op:\s*/i, "").replace(/\s*×\d+\s*$/, "");
2804
+ const m = clause.match(/^\s*(rm|rmdir)\b(.*)$/i);
2805
+ if (!m) {
2806
+ return false;
2807
+ }
2808
+ const operands = m[2].split(/\s+/).filter((t) => t && !t.startsWith("-"));
2809
+ if (operands.length === 0) {
2810
+ return false;
2811
+ }
2812
+ const receiptJson = (t) => /(?:^|\/)\.receipts\/[^/]+\.json$/.test(t.replace(/^["']|["']$/g, ""));
2813
+ return operands.every(receiptJson);
2814
+ }
2815
+
2816
+ // src/trace/diffScope.ts
2817
+ function git(args, cwd) {
2818
+ const r = spawnSync("git", args, { encoding: "utf8", cwd });
2819
+ return r.status === 0 ? r.stdout : null;
2820
+ }
2821
+ var RECEIPTS_DIR_RE = /(?:^|\/)\.receipts\//;
2822
+ function changedFiles(baseOverride, opts) {
2823
+ const root = git(["rev-parse", "--show-toplevel"])?.trim();
2824
+ if (!root) {
2825
+ return null;
2826
+ }
2827
+ let base4 = baseOverride;
2828
+ if (!base4) {
2829
+ const sym = git(["symbolic-ref", "refs/remotes/origin/HEAD"], root)?.trim();
2830
+ if (sym) {
2831
+ base4 = sym.replace("refs/remotes/", "");
2832
+ } else if (git(["rev-parse", "--verify", "--quiet", "origin/main"], root) !== null) {
2833
+ base4 = "origin/main";
2834
+ } else {
2835
+ base4 = "main";
2836
+ }
2837
+ }
2838
+ let out = git(["diff", "--name-only", `${base4}...HEAD`], root);
2839
+ if (out === null && !baseOverride) {
2840
+ out = git(["diff", "--name-only", "main...HEAD"], root);
2841
+ if (out !== null) {
2842
+ base4 = "main";
2843
+ }
2844
+ }
2845
+ const pending = opts?.includeWorkingTree ? workingTreeFiles(root) : [];
2846
+ if (out === null && pending.length === 0) {
2847
+ return null;
2848
+ }
2849
+ const files = [.../* @__PURE__ */ new Set([...(out ?? "").split("\n").map((s) => s.trim()), ...pending])].filter(Boolean).filter((f) => !RECEIPTS_DIR_RE.test(f)).sort();
2850
+ if (files.length === 0) {
2851
+ return null;
2852
+ }
2853
+ return { base: base4, files, repoRoot: root };
2854
+ }
2855
+ function workingTreeFiles(root) {
2856
+ const out = git(["status", "--porcelain", "-z", "--untracked-files=all"], root);
2857
+ if (!out) {
2858
+ return [];
2859
+ }
2860
+ const toks = out.split("\0");
2861
+ const files = [];
2862
+ for (let i = 0; i < toks.length; i++) {
2863
+ const t = toks[i];
2864
+ if (!t || t.length < 4 || t[2] !== " ") {
2865
+ continue;
2866
+ }
2867
+ files.push(t.slice(3));
2868
+ if (t[0] === "R" || t[0] === "C") {
2869
+ i++;
2870
+ }
2871
+ }
2872
+ return files;
2873
+ }
2874
+ function inDiff(filePath, files) {
2875
+ const base4 = filePath.split("/").pop();
2876
+ for (const d of files) {
2877
+ if (d === filePath || d.endsWith(`/${filePath}`) || filePath.endsWith(`/${d}`)) {
2878
+ return true;
2879
+ }
2880
+ if (base4 && d.split("/").pop() === base4) {
2881
+ return true;
2882
+ }
2883
+ }
2884
+ return false;
2885
+ }
2886
+ var PROCESS_PREFIXES = ["pipe-sh", "tool-fabricated", "tool-malformed-args"];
2887
+ var isProcessFinding = (id) => PROCESS_PREFIXES.some((p) => id === p || id.startsWith(`${p}-`));
2888
+ var isDestructive = (id) => id.startsWith("destructive-");
2889
+ var isGitProcess = (id) => id.startsWith("force-push") || id.startsWith("destructive-git");
2890
+ function hookBypassIsPushOnly(command) {
2891
+ const noVerify = parseGitInvocations(command).filter(isNoVerify);
2892
+ return noVerify.length > 0 && noVerify.every((inv) => inv.subcommand === "push");
2893
+ }
2894
+ function gitTouchesDiff(command, files) {
2895
+ for (const inv of parseGitInvocations(command)) {
2896
+ if (!destroysGitData(inv) && !rewritesHistory(inv)) {
2897
+ continue;
2898
+ }
2899
+ if (touchedPaths(inv).some((p) => inDiff(p, files))) {
2900
+ return true;
2901
+ }
2902
+ }
2903
+ return false;
2904
+ }
2905
+ function keepUnderDiff(f, files, commandFor, locusFor) {
2906
+ if (locusFor?.(f) === "elsewhere") {
2907
+ return false;
2908
+ }
2909
+ if (isProcessFinding(f.id)) {
2910
+ return true;
2911
+ }
2912
+ if (f.id === "hook-bypass" || f.id.startsWith("hook-bypass-")) {
2913
+ const cmd = commandFor?.(f);
2914
+ return cmd ? !hookBypassIsPushOnly(cmd) : true;
2915
+ }
2916
+ if (isGitProcess(f.id)) {
2917
+ const cmd = commandFor?.(f);
2918
+ return cmd ? gitTouchesDiff(cmd, files) : false;
2919
+ }
2920
+ if (isDestructive(f.id)) {
2921
+ if (destructiveOutsideRepo(f.title) || regenerableReceiptRm(f.title)) {
2922
+ return false;
2923
+ }
2924
+ const cmd = commandFor?.(f) ?? f.title.replace(/^Destructive op:\s*/i, "").replace(/\s*×\d+\s*$/, "");
2925
+ if (/(^|[\s;&|])git\s/.test(cmd)) {
2926
+ return gitTouchesDiff(cmd, files);
2927
+ }
2928
+ return true;
2929
+ }
2930
+ return f.filePath ? inDiff(f.filePath, files) : false;
2931
+ }
2932
+ function commandMentionsFile(cmd, files) {
2933
+ if (!cmd) {
2934
+ return false;
2935
+ }
2936
+ return files.some(
2937
+ (f) => f.includes("/") ? cmd.includes(f) : new RegExp(`(?:^|[\\s'"\`=/])${f.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`).test(cmd)
2938
+ );
2939
+ }
2940
+ function diffEffort(derived, files, window) {
2941
+ const editedGens = /* @__PURE__ */ new Set();
2942
+ derived.spans.forEach((s, i) => {
2943
+ if (s.kind !== "tool" || !s.parentSpanId) {
2944
+ return;
2945
+ }
2946
+ if (window && (i < window.start || i > window.end)) {
2947
+ return;
2948
+ }
2949
+ if (isEditTool(s.name)) {
2950
+ const fp = filePathOf(s.input);
2951
+ if (fp && inDiff(fp, files)) {
2952
+ editedGens.add(s.parentSpanId);
2953
+ }
2954
+ return;
2955
+ }
2956
+ if (isCommandTool(s.name) && commandMentionsFile(commandOf(s.input), files)) {
2957
+ editedGens.add(s.parentSpanId);
2958
+ }
2959
+ });
2960
+ let cost = 0;
2961
+ let tokens = 0;
2962
+ for (const s of derived.spans) {
2963
+ if (s.kind === "generation" && editedGens.has(s.spanId)) {
2964
+ cost += s.cost || estimateCost(s.tokens, s.name);
2965
+ tokens += s.tokens?.total ?? 0;
2966
+ }
2967
+ }
2968
+ return { cost, tokens, turns: editedGens.size };
2969
+ }
2970
+ function narrowEffort(sliceDerived, files) {
2971
+ if (!sliceDerived) {
2972
+ return null;
2973
+ }
2974
+ const eff = diffEffort(sliceDerived, files);
2975
+ return eff.turns > 0 ? eff : null;
2976
+ }
2977
+ function windowedEffort(derived, files, window) {
2978
+ const eff = diffEffort(derived, files, window);
2979
+ return eff.turns > 0 ? eff : null;
2980
+ }
2981
+ function applyDiffScope(derived, findings, files, projectPath) {
2982
+ const spanCmd = /* @__PURE__ */ new Map();
2983
+ const spanCwd = /* @__PURE__ */ new Map();
2984
+ for (const s of derived.spans) {
2985
+ if (s.spanId) {
2986
+ spanCmd.set(s.spanId, commandOf(s.input));
2987
+ if (s.cwd) {
2988
+ spanCwd.set(s.spanId, s.cwd);
2989
+ }
2990
+ }
2991
+ }
2992
+ const cmdOf = (f) => f.evidenceSpanId ? spanCmd.get(f.evidenceSpanId) : void 0;
2993
+ const locusOf = (f) => {
2994
+ if (!projectPath || !f.evidenceSpanId) {
2995
+ return void 0;
2996
+ }
2997
+ const base4 = spanCwd.get(f.evidenceSpanId);
2998
+ const state = cwdAtFirstGit(spanCmd.get(f.evidenceSpanId) ?? "", base4);
2999
+ if (state.kind === "unknown") {
3000
+ return "elsewhere";
3001
+ }
3002
+ const at = state.kind === "known" ? state.path : base4;
3003
+ if (!at) {
3004
+ return void 0;
3005
+ }
3006
+ return at === projectPath || at.startsWith(`${projectPath}/`) ? "here" : "elsewhere";
3007
+ };
3008
+ const keep = (f) => keepUnderDiff(f, files, cmdOf, locusOf);
3009
+ const scopedFindings = {
3010
+ main: findings.main.filter(keep),
3011
+ minor: findings.minor.filter(keep)
3012
+ };
3013
+ const filesChanged = derived.filesChanged.filter((fc) => inDiff(fc.path, files));
3014
+ const destructiveCount = derived.spans.filter((s) => s.destructive).filter((s) => {
3015
+ const cmd = commandOf(s.input);
3016
+ const clause = destructiveMatch(cmd) ?? cmd;
3017
+ if (destructiveOutsideRepo(clause) || regenerableReceiptRm(clause)) {
3018
+ return false;
3019
+ }
3020
+ if (/(^|[\s;&|])git\s/.test(cmd)) {
3021
+ return gitTouchesDiff(cmd, files);
3022
+ }
3023
+ return true;
3024
+ }).length;
3025
+ const eff = diffEffort(derived, files);
3026
+ return {
3027
+ derived: {
3028
+ ...derived,
3029
+ filesChanged,
3030
+ destructiveCount,
3031
+ diffCostUsd: eff.cost,
3032
+ diffTokens: eff.tokens,
3033
+ diffTurns: eff.turns
3034
+ },
3035
+ findings: scopedFindings
3036
+ };
3037
+ }
3038
+
2749
3039
  // src/version.ts
2750
3040
  import { readFileSync } from "fs";
2751
3041
  import { fileURLToPath } from "url";
@@ -2935,7 +3225,14 @@ function finalAssistantText(sum) {
2935
3225
  const gens = sum.spans.filter((s) => s.kind !== "session").sort((a, b) => a.startTime - b.startTime || a.spanId.localeCompare(b.spanId)).filter((s) => s.kind === "generation");
2936
3226
  return gens[gens.length - 1]?.input || "";
2937
3227
  }
2938
- function deriveEvidence(session, sum) {
3228
+ function samePath(touched, scopeFile) {
3229
+ if (touched === scopeFile || touched.endsWith(`/${scopeFile}`) || scopeFile.endsWith(`/${touched}`)) {
3230
+ return true;
3231
+ }
3232
+ const tb = touched.split("/").pop();
3233
+ return !!tb && tb === scopeFile.split("/").pop();
3234
+ }
3235
+ function deriveEvidence(session, sum, scope) {
2939
3236
  const tools = sum.spans.filter((s) => s.kind === "tool");
2940
3237
  const edits = tools.filter((s) => isEditTool(s.name)).length;
2941
3238
  const commandSpans = tools.filter((s) => isCommandTool(s.name));
@@ -2972,6 +3269,14 @@ function deriveEvidence(session, sum) {
2972
3269
  finishReasons[s.finishReason] = (finishReasons[s.finishReason] ?? 0) + 1;
2973
3270
  }
2974
3271
  }
3272
+ let coveredFiles;
3273
+ if (scope?.kind === "diff" && scope.files?.length) {
3274
+ const touched = tools.filter((s) => isEditTool(s.name) || isReadTool(s.name)).map((s) => filePathOf(s.input)).filter((f) => !!f);
3275
+ const cmdTexts = commandSpans.map((s) => commandOf(s.input)).filter(Boolean);
3276
+ coveredFiles = scope.files.filter(
3277
+ (f) => touched.some((t) => samePath(t, f)) || cmdTexts.some((c) => commandMentionsFile(c, [f]))
3278
+ ).length;
3279
+ }
2975
3280
  const tk = session.totals.tokens;
2976
3281
  return {
2977
3282
  filesChanged: sum.filesChanged.length,
@@ -2979,9 +3284,12 @@ function deriveEvidence(session, sum) {
2979
3284
  commands: commandSpans.length,
2980
3285
  reads,
2981
3286
  destructiveOps: sum.destructiveCount,
3287
+ ...coveredFiles != null ? { coveredFiles } : {},
2982
3288
  testsRan,
2983
3289
  ...sum.diffCostUsd != null ? {
2984
- diffCostUsd: round(sum.diffCostUsd, 2),
3290
+ // sub-cent costs keep 4dp — 2dp rounding recorded $0.004 work as $0.00
3291
+ // (field: every release receipt showed "$0.00 (this change)")
3292
+ diffCostUsd: round(sum.diffCostUsd, 2) || round(sum.diffCostUsd, 4),
2985
3293
  diffTokens: sum.diffTokens ?? 0,
2986
3294
  diffTurns: sum.diffTurns ?? 0
2987
3295
  } : {},
@@ -2997,7 +3305,8 @@ function deriveEvidence(session, sum) {
2997
3305
  reasoning: tk.reasoning,
2998
3306
  total: tk.total
2999
3307
  },
3000
- costUsd: round(sum.totalCost, 2),
3308
+ // same sub-cent fallback as diffCostUsd, so diff ≤ session always holds
3309
+ costUsd: round(sum.totalCost, 2) || round(sum.totalCost, 4),
3001
3310
  cacheHitRatio: round(sum.cacheHitRatio, 4),
3002
3311
  cacheWriteRatio: round(tk.cacheWrite / Math.max(1, tk.cacheRead), 4)
3003
3312
  };
@@ -3015,7 +3324,7 @@ async function buildReceipt(session, derived, findings, opts = {}) {
3015
3324
  durationMs: session.totals.durationMs
3016
3325
  },
3017
3326
  grade: gradeLetter(findings.main),
3018
- evidence: deriveEvidence(session, derived),
3327
+ evidence: deriveEvidence(session, derived, opts.scope),
3019
3328
  findings: [...findings.main, ...findings.minor].map(toReceiptFinding),
3020
3329
  generator: {
3021
3330
  name: "altimate-receipts",
@@ -3097,10 +3406,10 @@ var dur = (ms) => {
3097
3406
  };
3098
3407
  function grade(c, main) {
3099
3408
  const meta = {
3100
- F: { col: c.bgRed + c.white, verdict: "DO NOT MERGE WITHOUT REVIEW", icon: "\u26D4" },
3101
- C: { col: c.bgYel + c.black, verdict: "NEEDS A CLOSE REVIEW", icon: "\u26A0\uFE0F " },
3102
- B: { col: c.bgGrn + c.black, verdict: "MINOR THINGS TO CHECK", icon: "\u{1F50D}" },
3103
- A: { col: c.bgGrn + c.white, verdict: "LOOKS CLEAN", icon: "\u2705" }
3409
+ F: { col: c.bgRed + c.white, verdict: "CRITICAL EVENTS ON RECORD", icon: "\u26D4" },
3410
+ C: { col: c.bgYel + c.black, verdict: "HIGH-SEVERITY EVENTS ON RECORD", icon: "\u26A0\uFE0F " },
3411
+ B: { col: c.bgGrn + c.black, verdict: "EVENTS ON RECORD", icon: "\u{1F50D}" },
3412
+ A: { col: c.bgGrn + c.white, verdict: "NOTHING DETECTED", icon: "\u2705" }
3104
3413
  };
3105
3414
  const g = gradeLetter(main);
3106
3415
  return { g, ...meta[g] };
@@ -3146,7 +3455,7 @@ function renderCard(args, opts = {}) {
3146
3455
  )
3147
3456
  );
3148
3457
  out.push("");
3149
- out.push(line(`${c.gray}\u250C\u2500 VERDICT ${"\u2500".repeat(W - 10)}\u2510${c.reset}`));
3458
+ out.push(line(`${c.gray}\u250C\u2500 RECORD ${"\u2500".repeat(W - 9)}\u2510${c.reset}`));
3150
3459
  const gradeInner = `${c.gray} ${c.reset}${gd.col}${c.bold} ${gd.g} ${c.reset} ${gd.icon} ${c.bold}${gd.verdict}${c.reset}`;
3151
3460
  out.push(line(`${c.gray}\u2502${c.reset}${pad(gradeInner, W)}${c.gray}\u2502${c.reset}`));
3152
3461
  const counts = ["critical", "high", "medium"].map((s) => {
@@ -4112,7 +4421,7 @@ import { homedir as homedir2 } from "os";
4112
4421
  import { join as join2 } from "path";
4113
4422
 
4114
4423
  // src/trace/sqlite.ts
4115
- import { spawnSync } from "child_process";
4424
+ import { spawnSync as spawnSync2 } from "child_process";
4116
4425
  async function tryNodeSqlite(dbPath2) {
4117
4426
  try {
4118
4427
  const { DatabaseSync } = await import("sqlite");
@@ -4126,13 +4435,13 @@ async function tryNodeSqlite(dbPath2) {
4126
4435
  }
4127
4436
  }
4128
4437
  function trySqlite3Cli(dbPath2) {
4129
- const probe = spawnSync("sqlite3", ["-version"], { encoding: "utf8" });
4438
+ const probe = spawnSync2("sqlite3", ["-version"], { encoding: "utf8" });
4130
4439
  if (probe.error || probe.status !== 0) {
4131
4440
  return null;
4132
4441
  }
4133
4442
  return {
4134
4443
  all: (sql) => {
4135
- const r = spawnSync("sqlite3", ["-readonly", "-json", dbPath2, sql], {
4444
+ const r = spawnSync2("sqlite3", ["-readonly", "-json", dbPath2, sql], {
4136
4445
  encoding: "utf8",
4137
4446
  maxBuffer: 1 << 29
4138
4447
  // bubbles can be large
@@ -4684,23 +4993,21 @@ function redactReceipt(receipt) {
4684
4993
  }
4685
4994
 
4686
4995
  export {
4687
- estimateCost,
4688
- isEditTool,
4689
- filePathOf,
4690
- commandOf,
4691
- destructiveMatch,
4692
4996
  deriveSpans,
4693
4997
  cwdAtFirstGit,
4694
- parseGitInvocations,
4695
- isNoVerify,
4696
- rewritesHistory,
4697
- destroysGitData,
4698
- touchedPaths,
4699
4998
  formatTokens,
4700
4999
  formatCostAlways,
4701
5000
  deriveFindings,
4702
5001
  gradeLetter,
4703
5002
  renderLedger,
5003
+ privileged,
5004
+ findingSurface,
5005
+ destructiveOutsideRepo,
5006
+ changedFiles,
5007
+ inDiff,
5008
+ narrowEffort,
5009
+ windowedEffort,
5010
+ applyDiffScope,
4704
5011
  getVersion,
4705
5012
  hashTranscriptFile,
4706
5013
  sha256Hex,
@@ -4730,4 +5037,4 @@ export {
4730
5037
  redact,
4731
5038
  redactReceipt
4732
5039
  };
4733
- //# sourceMappingURL=chunk-72Y2GS7I.js.map
5040
+ //# sourceMappingURL=chunk-SGVU3CGP.js.map