altimate-receipts 0.5.1 → 0.6.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.
@@ -7,7 +7,7 @@ import {
7
7
  loadSession,
8
8
  selectSummary,
9
9
  upsertSection
10
- } from "./chunk-EYM5WETZ.js";
10
+ } from "./chunk-DBQWQVZZ.js";
11
11
 
12
12
  // src/report/sessions.ts
13
13
  async function deriveTargets(opts) {
@@ -259,4 +259,4 @@ export {
259
259
  renderTrends,
260
260
  upsertTrendsSection
261
261
  };
262
- //# sourceMappingURL=chunk-EKMFU3ES.js.map
262
+ //# sourceMappingURL=chunk-543NGQTN.js.map
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ agentIds,
3
4
  buildReceipt,
4
5
  commandOf,
5
6
  cwdAtFirstGit,
@@ -19,7 +20,7 @@ import {
19
20
  redactReceipt,
20
21
  rewritesHistory,
21
22
  touchedPaths
22
- } from "./chunk-EYM5WETZ.js";
23
+ } from "./chunk-DBQWQVZZ.js";
23
24
 
24
25
  // src/receipt/canonical.ts
25
26
  function canonicalize(value) {
@@ -416,6 +417,10 @@ function sliceByBranch(session, branch) {
416
417
  }
417
418
 
418
419
  // src/sign/rederive.ts
420
+ function sourceFromReceipt(receipt) {
421
+ const prefix = receipt.subject?.[0]?.name?.split("/")[0];
422
+ return agentIds().find((id) => id === prefix);
423
+ }
419
424
  async function rederiveFromTranscript(path, opts = {}) {
420
425
  const loaded = await loadById(opts.source ?? "claude-code", path);
421
426
  if (!loaded) {
@@ -450,6 +455,10 @@ async function compareToTranscript(committed, transcriptPath, opts = {}) {
450
455
  const fresh = await rederiveFromTranscript(transcriptPath, {
451
456
  redact: true,
452
457
  ...opts,
458
+ // Re-derive through the adapter the receipt names (its subject prefix), so a
459
+ // codex/cursor receipt isn't re-run through the Claude adapter. An explicit
460
+ // opts.source still wins.
461
+ source: opts.source ?? sourceFromReceipt(committed),
453
462
  scope: committed.predicate.scope
454
463
  });
455
464
  if (!fresh) {
@@ -569,4 +578,4 @@ export {
569
578
  rederiveFromTranscript,
570
579
  compareToTranscript
571
580
  };
572
- //# sourceMappingURL=chunk-GOLRNSZT.js.map
581
+ //# sourceMappingURL=chunk-ASPLZPMQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/receipt/canonical.ts","../src/share/clipboard.ts","../src/share/markdown.ts","../src/sign/envelope.ts","../src/trace/diffScope.ts","../src/findings/surface.ts","../src/trace/slice.ts","../src/sign/rederive.ts","../src/report/checks.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 { 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 VERDICT: Record<GradeLetter, string> = {\n F: \"DO NOT MERGE WITHOUT REVIEW\",\n C: \"NEEDS A CLOSE REVIEW\",\n B: \"MINOR THINGS TO CHECK\",\n A: \"LOOKS CLEAN\",\n};\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(`**${VERDICT[g]}** · ${counts} · _${summary.source}_`);\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 { estimateCost } from \"../findings/cost.js\";\nimport type { Finding, FindingSet } from \"../findings/findings.js\";\nimport {\n cwdAtFirstGit,\n destroysGitData,\n isNoVerify,\n parseGitInvocations,\n rewritesHistory,\n touchedPaths,\n} from \"../findings/gitParse.js\";\nimport { type DerivedSummary, destructiveMatch } from \"../findings/spans.js\";\nimport { destructiveOutsideRepo, regenerableReceiptRm } from \"../findings/surface.js\";\nimport { commandOf, filePathOf, isEditTool } from \"../findings/toolRoles.js\";\n\n/**\n * Diff-scoping for `receipts pr` (SPEC-0013). The PR's changed-file set is read from\n * git at produce time and recorded in `predicate.scope.files`; every scoping decision\n * below is a pure function of that file list (+ the repo-root-free destructive\n * heuristic), so a verifier reproduces the exact scoped receipt from the recorded\n * `files` alone — no git at verify time. That's what keeps L1 re-derivation intact.\n */\n\nfunction git(args: string[], cwd?: string): string | null {\n const r = spawnSync(\"git\", args, { encoding: \"utf8\", cwd });\n return r.status === 0 ? r.stdout : null;\n}\n\nexport interface DiffResult {\n /** the ref the diff was taken against */\n base: string;\n /** changed files, sorted, repo-relative */\n files: string[];\n repoRoot: string;\n}\n\n/** Receipts' own bookkeeping dir. A receipt must NOT scope over `.receipts/` — else\n * committing the receipt changes the diff, which re-scopes the next receipt: an endless\n * regeneration loop in the pre-push hook. Excluded from the diff scope here AND in the\n * Action's scope-honesty check (action/verified-by.mjs) so the two stay consistent. */\nexport const RECEIPTS_DIR_RE = /(?:^|\\/)\\.receipts\\//;\n\n/**\n * The PR's changed files via `git diff --name-only <base>...HEAD`, sorted, with the\n * `.receipts/` bookkeeping dir excluded (see RECEIPTS_DIR_RE). `base` defaults to the\n * default branch (origin/HEAD → main). Returns null when no repo, base, or non-empty\n * diff is resolvable (caller falls back honestly).\n */\nexport function changedFiles(baseOverride?: string): DiffResult | null {\n const root = git([\"rev-parse\", \"--show-toplevel\"])?.trim();\n if (!root) {\n return null;\n }\n let base = baseOverride;\n if (!base) {\n const sym = git([\"symbolic-ref\", \"refs/remotes/origin/HEAD\"], root)?.trim();\n base = sym ? sym.replace(\"refs/remotes/\", \"\") : \"main\";\n }\n let out = git([\"diff\", \"--name-only\", `${base}...HEAD`], root);\n if (out === null && !baseOverride) {\n // base may be unfetched (e.g. origin/main absent locally) — try local main.\n out = git([\"diff\", \"--name-only\", \"main...HEAD\"], root);\n if (out !== null) {\n base = \"main\";\n }\n }\n if (out === null) {\n return null;\n }\n const files = out\n .split(\"\\n\")\n .map((s) => s.trim())\n .filter(Boolean)\n .filter((f) => !RECEIPTS_DIR_RE.test(f)) // a receipt never scopes over .receipts/\n .sort();\n if (files.length === 0) {\n return null; // nothing to scope to (or a receipts-only diff) → caller falls back\n }\n return { base, files, repoRoot: root };\n}\n\n/** Does a finding's file path belong to the changed-file set? Matches exact, path\n * suffix, or basename (a PR's diff is small; basename collisions are unlikely and\n * fail toward surfacing). */\nexport function inDiff(filePath: string, files: readonly string[]): boolean {\n const base = filePath.split(\"/\").pop();\n for (const d of files) {\n if (d === filePath || d.endsWith(`/${filePath}`) || filePath.endsWith(`/${d}`)) {\n return true;\n }\n if (base && d.split(\"/\").pop() === base) {\n return true;\n }\n }\n return false;\n}\n\n// Integrity findings that are about the *act*, not a path — kept regardless of the\n// diff (a `curl | sh`, a fabricated tool call, a `--no-verify` are merge-relevant\n// even though they name no changed file). `force-push` is NOT here: it's a git op\n// scoped by its touched paths below (it touches none ⇒ session hygiene, not a merge\n// risk for this change).\n// Path-free integrity findings kept regardless of the diff — a `curl|sh` or a\n// fabricated/malformed tool call is a session-integrity problem no matter which files\n// changed. `hook-bypass` is NOT here: it's scoped below (a `push --no-verify` only skips\n// pre-push hooks ⇒ session hygiene, not a merge risk for this diff).\nconst PROCESS_PREFIXES = [\"pipe-sh\", \"tool-fabricated\", \"tool-malformed-args\"];\nconst isProcessFinding = (id: string): boolean =>\n PROCESS_PREFIXES.some((p) => id === p || id.startsWith(`${p}-`));\nconst isDestructive = (id: string): boolean => id.startsWith(\"destructive-\");\nconst isGitProcess = (id: string): boolean =>\n id.startsWith(\"force-push\") || id.startsWith(\"destructive-git\");\n\n/** True when the **destructive** git op in this command touches a diff file. Only the\n * destructive / history-rewriting invocation is judged — a benign sibling clause in the\n * same compound command (e.g. `git add <diff-file> && git reset --hard <ref>`) must NOT\n * make the unrelated reset look diff-relevant. Ref/history/worktree-wide ops\n * (`reset --hard <branch>`, `push -f`, `branch -D`) have no touched path ⇒ false ⇒\n * session hygiene, not a merge risk for this change. */\n/** True when every `--no-verify` in this command is on a `push` (no `commit --no-verify`).\n * A `push --no-verify` skips only the pre-push hooks (no committed content changes), so it\n * is session hygiene, not a merge risk for this diff. A `commit --no-verify` skipped the\n * pre-commit checks on committed content ⇒ NOT push-only ⇒ kept. */\nfunction hookBypassIsPushOnly(command: string): boolean {\n const noVerify = parseGitInvocations(command).filter(isNoVerify);\n return noVerify.length > 0 && noVerify.every((inv) => inv.subcommand === \"push\");\n}\n\nfunction gitTouchesDiff(command: string, files: readonly string[]): boolean {\n for (const inv of parseGitInvocations(command)) {\n if (!destroysGitData(inv) && !rewritesHistory(inv)) {\n continue; // a `git add`/`commit`/`checkout <branch>` clause is not the destructive op\n }\n if (touchedPaths(inv).some((p) => inDiff(p, files))) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Keep a finding under a diff scope:\n * - path-free integrity findings (hook-bypass, pipe-sh, fabricated/malformed) — always,\n * - a git process/destructive op only when its touched paths fall in the diff\n * (`reset --hard <branch>`/`push -f` touch nothing ⇒ dropped to the session card),\n * - a non-git destructive whose target is a repo path (not a `$TMP`/`.receipts` scratch),\n * - any other finding about a changed file.\n * Pure function of the finding + the recorded file list (+ the finding's own command,\n * itself re-derived from the transcript) ⇒ reproducible at verify.\n */\nexport function keepUnderDiff(\n f: Pick<Finding, \"id\" | \"title\"> & { filePath?: string; evidenceSpanId?: string },\n files: readonly string[],\n commandFor?: (f: Pick<Finding, \"id\" | \"evidenceSpanId\">) => string | undefined,\n locusFor?: (f: Pick<Finding, \"id\" | \"evidenceSpanId\">) => \"here\" | \"elsewhere\" | undefined,\n): boolean {\n // Cross-repo bleed (SPEC-0069 addendum, observed live): a session that also ran\n // `git commit --no-verify` in ANOTHER repo (`cd /other && git commit --no-verify`)\n // must not flag THIS repo's diff receipt. The locus is resolved by the caller from\n // transcript data only (span cwd + cd tracking), so the drop re-derives with no\n // git at verify time; \"elsewhere\" includes unresolvable-cd uncertainty (under-claim).\n if (locusFor?.(f) === \"elsewhere\") {\n return false;\n }\n if (isProcessFinding(f.id)) {\n return true;\n }\n // hook-bypass: a `push --no-verify` skips only pre-push hooks (no code change) ⇒ session\n // hygiene, dropped from the diff; a `commit --no-verify` (or an unresolvable command) is\n // kept — it may have skipped pre-commit checks on the committed change.\n if (f.id === \"hook-bypass\" || f.id.startsWith(\"hook-bypass-\")) {\n const cmd = commandFor?.(f);\n return cmd ? !hookBypassIsPushOnly(cmd) : true;\n }\n // git ref/history surgery is a merge risk only if it touched a changed file.\n if (isGitProcess(f.id)) {\n const cmd = commandFor?.(f);\n return cmd ? gitTouchesDiff(cmd, files) : false;\n }\n if (isDestructive(f.id)) {\n // scratch (`/tmp`) and the tool's own regenerable `.receipts/*.json` are both\n // out-of-scope for \"did this change do damage?\" — drop either from the diff.\n if (destructiveOutsideRepo(f.title) || regenerableReceiptRm(f.title)) {\n return false;\n }\n // a git destructive (`git reset --hard <branch>`, `git rm`) only counts when it hit\n // the diff; a non-git filesystem removal (`rm -rf src/old`) keeps the repo-path\n // behaviour. The command comes from the span resolver, falling back to the title's\n // clause (`Destructive op: <cmd>`), so this holds even without a resolver.\n const cmd =\n commandFor?.(f) ?? f.title.replace(/^Destructive op:\\s*/i, \"\").replace(/\\s*×\\d+\\s*$/, \"\");\n if (/(^|[\\s;&|])git\\s/.test(cmd)) {\n return gitTouchesDiff(cmd, files);\n }\n return true;\n }\n return f.filePath ? inDiff(f.filePath, files) : false;\n}\n\nexport interface ScopedData {\n derived: DerivedSummary;\n findings: FindingSet;\n}\n\n/**\n * Cost/tokens of just the generation turns that edited a file in the diff — \"what\n * producing *this change* cost\", versus the whole-session total. Each edit tool span\n * links to the assistant turn that emitted it via `parentSpanId`; we collect those\n * generations and sum their recorded/estimated cost + tokens. Pure function of\n * `derived.spans` (re-derived from the transcript) + `files` ⇒ reproducible at verify.\n *\n * Honest bound: a turn that edited a diff file *and* others is counted whole, and the\n * turn's cost includes its shared context overhead — so this is an upper estimate of the\n * diff's marginal cost, not a precise split. It is always ≤ the session total and, in a\n * session that produced several PRs, far below it.\n */\nexport function diffEffort(\n derived: DerivedSummary,\n files: readonly string[],\n): { cost: number; tokens: number; turns: number } {\n const editedGens = new Set<string>();\n for (const s of derived.spans) {\n if (s.kind !== \"tool\" || !s.parentSpanId || !isEditTool(s.name)) {\n continue;\n }\n const fp = filePathOf(s.input);\n if (fp && inDiff(fp, files)) {\n editedGens.add(s.parentSpanId);\n }\n }\n let cost = 0;\n let tokens = 0;\n for (const s of derived.spans) {\n if (s.kind === \"generation\" && editedGens.has(s.spanId)) {\n // recorded cost when present, else estimate from the turn's tokens + model (the\n // generation span's name is the model id) — the same basis as the session total.\n cost += s.cost || estimateCost(s.tokens, s.name);\n tokens += s.tokens?.total ?? 0;\n }\n }\n return { cost, tokens, turns: editedGens.size };\n}\n\n/**\n * Branch-narrowed effort (SPEC-0068 R4): in a session that produced several PRs, the\n * whole-session `diffEffort` counts every turn that ever touched a hot file (cli.ts,\n * README) — observed 26× inflation on a marathon session. When the session carries the\n * receipt branch's turns, the honest bound is branch turns ∩ diff-file edits. Returns\n * null when there is no slice or the slice has zero diff-file-editing turns (e.g.\n * Bash-only writes) — callers then keep the whole-session bound and must NOT record\n * `scope.branch`, so the receipt always states how its effort was computed. Shared by\n * produce (`receipts pr`) and verify (rederive) so the bytes match (L1).\n */\nexport function narrowEffort(\n sliceDerived: DerivedSummary | null,\n files: readonly string[],\n): { cost: number; tokens: number; turns: number } | null {\n if (!sliceDerived) {\n return null;\n }\n const eff = diffEffort(sliceDerived, files);\n return eff.turns > 0 ? eff : null;\n}\n\n/**\n * Apply a diff scope to derived evidence + findings. Scopes the risk-relevant\n * evidence (`filesChanged`, `destructiveOps`) to the change and computes the diff's own\n * cost (`diffCostUsd`/`diffTokens`/`diffTurns`); leaves whole-session effort metrics\n * (`edits`/`commands`/`reads`/total tokens/cost) intact for the footer. Deterministic;\n * uses only `files` so re-derivation reproduces it from `predicate.scope.files`.\n */\nexport function applyDiffScope(\n derived: DerivedSummary,\n findings: FindingSet,\n files: readonly string[],\n projectPath?: string,\n): ScopedData {\n // evidenceSpanId → the span's command/cwd, so a git process/destructive finding can\n // be attributed to the diff by its touched paths and its repo. Built from\n // `derived.spans` (re-derived from the transcript) so the scope stays a pure\n // function of (derived, findings, files, projectPath) — all transcript-derived.\n const spanCmd = new Map<string, string>();\n const spanCwd = new Map<string, string>();\n for (const s of derived.spans) {\n if (s.spanId) {\n spanCmd.set(s.spanId, commandOf(s.input));\n if (s.cwd) {\n spanCwd.set(s.spanId, s.cwd);\n }\n }\n }\n const cmdOf = (f: Pick<Finding, \"evidenceSpanId\">): string | undefined =>\n f.evidenceSpanId ? spanCmd.get(f.evidenceSpanId) : undefined;\n // Did this finding's command actually run in the session's own project?\n // `cwdAtFirstGit` tracks `cd` clauses inside compounds; outside or uncertain ⇒\n // \"elsewhere\" (another repo's process risk — drop from the diff receipt).\n const locusOf = (f: Pick<Finding, \"evidenceSpanId\">): \"here\" | \"elsewhere\" | undefined => {\n if (!projectPath || !f.evidenceSpanId) {\n return undefined;\n }\n const base = spanCwd.get(f.evidenceSpanId);\n const state = cwdAtFirstGit(spanCmd.get(f.evidenceSpanId) ?? \"\", base);\n if (state.kind === \"unknown\") {\n return \"elsewhere\";\n }\n const at = state.kind === \"known\" ? state.path : base;\n if (!at) {\n return undefined;\n }\n return at === projectPath || at.startsWith(`${projectPath}/`) ? \"here\" : \"elsewhere\";\n };\n const keep = (f: Finding): boolean => keepUnderDiff(f, files, cmdOf, locusOf);\n const scopedFindings: FindingSet = {\n main: findings.main.filter(keep),\n minor: findings.minor.filter(keep),\n };\n const filesChanged = derived.filesChanged.filter((fc) => inDiff(fc.path, files));\n const destructiveCount = derived.spans\n .filter((s) => s.destructive)\n .filter((s) => {\n const cmd = commandOf(s.input);\n const clause = destructiveMatch(cmd) ?? cmd;\n if (destructiveOutsideRepo(clause) || regenerableReceiptRm(clause)) {\n return false;\n }\n // git ref/history surgery that touched no changed file isn't this diff's damage.\n if (/(^|[\\s;&|])git\\s/.test(cmd)) {\n return gitTouchesDiff(cmd, files);\n }\n return true;\n }).length;\n const eff = diffEffort(derived, files);\n return {\n derived: {\n ...derived,\n filesChanged,\n destructiveCount,\n diffCostUsd: eff.cost,\n diffTokens: eff.tokens,\n diffTurns: eff.turns,\n },\n findings: scopedFindings,\n };\n}\n","/**\n * Which surface a finding belongs to. The **merge gate** (the PR comment) is about\n * the *merged artifact* — what a reviewer must check before merging. The **operator**\n * view (the `receipts` card and `receipts trends`) is about *how the agent worked* —\n * efficiency, cost, loops, retries. One source of truth, shared by the PR-comment\n * renderer and its standalone Action mirror.\n */\n\n/** Kinds that describe agent behaviour/efficiency, not the merged change. */\nconst OPERATOR_KINDS = new Set([\n \"loop\",\n \"bottleneck\",\n \"cost-concentration\",\n \"cache-opportunity\",\n \"model-downgrade\",\n]);\n\nexport type Surface = \"merge\" | \"operator\";\n\nexport function findingSurface(id: string): Surface {\n // `errcluster-<tool>` (\"X failed N× before succeeding\") is an efficiency signal.\n if (OPERATOR_KINDS.has(id) || id.startsWith(\"errcluster-\")) {\n return \"operator\";\n }\n return \"merge\";\n}\n\n/**\n * True when a destructive finding's target is a **scratch path** (a `$VAR`, `/tmp`,\n * `/var/folders`, or `~`), so it can't be a merge risk and shouldn't gate the PR.\n * Parsed from the finding title's clause (the `destructiveMatch` output, e.g.\n * `Destructive op: rm -rf \"$TMP\"`). Conservative: only returns true when **every**\n * `rm`/`rmdir` operand is clearly non-repo — `rm -rf ./src`, `rm -rf build`,\n * `git reset --hard`, `DROP TABLE` are all kept (return false).\n */\nexport function destructiveOutsideRepo(title: string): boolean {\n const clause = title.replace(/^Destructive op:\\s*/i, \"\").replace(/\\s*×\\d+\\s*$/, \"\");\n // Only filesystem removals carry a path target we can judge; git/SQL/infra\n // destructives act on the repo or an external system → never \"outside repo\".\n const m = clause.match(/^\\s*(rm|rmdir)\\b(.*)$/i);\n if (!m) {\n return false;\n }\n const operands = m[2].split(/\\s+/).filter((t) => t && !t.startsWith(\"-\"));\n if (operands.length === 0) {\n return false;\n }\n const scratch = (t: string): boolean => {\n const p = t.replace(/^[\"']|[\"']$/g, \"\");\n return (\n /\\$/.test(p) ||\n p.startsWith(\"/tmp\") ||\n p.startsWith(\"/var/folders\") ||\n p.startsWith(\"/private/var/folders\") ||\n p.startsWith(\"~\")\n );\n };\n return operands.every(scratch);\n}\n\n/**\n * True when a destructive finding deletes only the tool's **own regenerable Receipt\n * artifact** (`rm .receipts/<branch>.json`), e.g. when regenerating it against a\n * corrected base. Like {@link destructiveOutsideRepo} this is a scope-layer carve-out:\n * a `.receipts/*.json` is inside the repo but is rewritten by `receipts pr`, so it\n * isn't a merge risk in the change under review. Conservative: only `rm`/`rmdir` whose\n * **every** operand is a `.receipts/<name>.json` file — a mixed command\n * (`rm .receipts/x.json src/app.ts`) or the whole dir (`rm -rf .receipts`) returns\n * false and keeps flagging.\n */\nexport function regenerableReceiptRm(title: string): boolean {\n const clause = title.replace(/^Destructive op:\\s*/i, \"\").replace(/\\s*×\\d+\\s*$/, \"\");\n const m = clause.match(/^\\s*(rm|rmdir)\\b(.*)$/i);\n if (!m) {\n return false;\n }\n const operands = m[2].split(/\\s+/).filter((t) => t && !t.startsWith(\"-\"));\n if (operands.length === 0) {\n return false;\n }\n // a flat `.receipts/<name>.json` file (receipts are one-per-branch, not nested) —\n // the dir itself (`.receipts`, `.receipts/`) has no `.json` operand, so it's kept.\n const receiptJson = (t: string): boolean =>\n /(?:^|\\/)\\.receipts\\/[^/]+\\.json$/.test(t.replace(/^[\"']|[\"']$/g, \"\"));\n return operands.every(receiptJson);\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 { applyDiffScope, narrowEffort } from \"../trace/diffScope.js\";\nimport { loadById } from \"../trace/load.js\";\nimport { sliceByBranch } from \"../trace/slice.js\";\nimport type { AgentSource } from \"../trace/types.js\";\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 const eff = narrowEffort(slice ? deriveSpans(slice) : null, scope.files);\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 scope: committed.predicate.scope,\n });\n if (!fresh) {\n return { rederived: false, matches: false };\n }\n return { rederived: true, matches: canonicalize(committed) === canonicalize(fresh) };\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"],"mappings":"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","names":["spawnSync","spawnSync","base"]}
1
+ {"version":3,"sources":["../src/receipt/canonical.ts","../src/share/clipboard.ts","../src/share/markdown.ts","../src/sign/envelope.ts","../src/trace/diffScope.ts","../src/findings/surface.ts","../src/trace/slice.ts","../src/sign/rederive.ts","../src/report/checks.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 { 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 VERDICT: Record<GradeLetter, string> = {\n F: \"DO NOT MERGE WITHOUT REVIEW\",\n C: \"NEEDS A CLOSE REVIEW\",\n B: \"MINOR THINGS TO CHECK\",\n A: \"LOOKS CLEAN\",\n};\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(`**${VERDICT[g]}** · ${counts} · _${summary.source}_`);\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 { estimateCost } from \"../findings/cost.js\";\nimport type { Finding, FindingSet } from \"../findings/findings.js\";\nimport {\n cwdAtFirstGit,\n destroysGitData,\n isNoVerify,\n parseGitInvocations,\n rewritesHistory,\n touchedPaths,\n} from \"../findings/gitParse.js\";\nimport { type DerivedSummary, destructiveMatch } from \"../findings/spans.js\";\nimport { destructiveOutsideRepo, regenerableReceiptRm } from \"../findings/surface.js\";\nimport { commandOf, filePathOf, isEditTool } from \"../findings/toolRoles.js\";\n\n/**\n * Diff-scoping for `receipts pr` (SPEC-0013). The PR's changed-file set is read from\n * git at produce time and recorded in `predicate.scope.files`; every scoping decision\n * below is a pure function of that file list (+ the repo-root-free destructive\n * heuristic), so a verifier reproduces the exact scoped receipt from the recorded\n * `files` alone — no git at verify time. That's what keeps L1 re-derivation intact.\n */\n\nfunction git(args: string[], cwd?: string): string | null {\n const r = spawnSync(\"git\", args, { encoding: \"utf8\", cwd });\n return r.status === 0 ? r.stdout : null;\n}\n\nexport interface DiffResult {\n /** the ref the diff was taken against */\n base: string;\n /** changed files, sorted, repo-relative */\n files: string[];\n repoRoot: string;\n}\n\n/** Receipts' own bookkeeping dir. A receipt must NOT scope over `.receipts/` — else\n * committing the receipt changes the diff, which re-scopes the next receipt: an endless\n * regeneration loop in the pre-push hook. Excluded from the diff scope here AND in the\n * Action's scope-honesty check (action/verified-by.mjs) so the two stay consistent. */\nexport const RECEIPTS_DIR_RE = /(?:^|\\/)\\.receipts\\//;\n\n/**\n * The PR's changed files via `git diff --name-only <base>...HEAD`, sorted, with the\n * `.receipts/` bookkeeping dir excluded (see RECEIPTS_DIR_RE). `base` defaults to the\n * default branch (origin/HEAD → main). Returns null when no repo, base, or non-empty\n * diff is resolvable (caller falls back honestly).\n */\nexport function changedFiles(baseOverride?: string): DiffResult | null {\n const root = git([\"rev-parse\", \"--show-toplevel\"])?.trim();\n if (!root) {\n return null;\n }\n let base = baseOverride;\n if (!base) {\n const sym = git([\"symbolic-ref\", \"refs/remotes/origin/HEAD\"], root)?.trim();\n base = sym ? sym.replace(\"refs/remotes/\", \"\") : \"main\";\n }\n let out = git([\"diff\", \"--name-only\", `${base}...HEAD`], root);\n if (out === null && !baseOverride) {\n // base may be unfetched (e.g. origin/main absent locally) — try local main.\n out = git([\"diff\", \"--name-only\", \"main...HEAD\"], root);\n if (out !== null) {\n base = \"main\";\n }\n }\n if (out === null) {\n return null;\n }\n const files = out\n .split(\"\\n\")\n .map((s) => s.trim())\n .filter(Boolean)\n .filter((f) => !RECEIPTS_DIR_RE.test(f)) // a receipt never scopes over .receipts/\n .sort();\n if (files.length === 0) {\n return null; // nothing to scope to (or a receipts-only diff) → caller falls back\n }\n return { base, files, repoRoot: root };\n}\n\n/** Does a finding's file path belong to the changed-file set? Matches exact, path\n * suffix, or basename (a PR's diff is small; basename collisions are unlikely and\n * fail toward surfacing). */\nexport function inDiff(filePath: string, files: readonly string[]): boolean {\n const base = filePath.split(\"/\").pop();\n for (const d of files) {\n if (d === filePath || d.endsWith(`/${filePath}`) || filePath.endsWith(`/${d}`)) {\n return true;\n }\n if (base && d.split(\"/\").pop() === base) {\n return true;\n }\n }\n return false;\n}\n\n// Integrity findings that are about the *act*, not a path — kept regardless of the\n// diff (a `curl | sh`, a fabricated tool call, a `--no-verify` are merge-relevant\n// even though they name no changed file). `force-push` is NOT here: it's a git op\n// scoped by its touched paths below (it touches none ⇒ session hygiene, not a merge\n// risk for this change).\n// Path-free integrity findings kept regardless of the diff — a `curl|sh` or a\n// fabricated/malformed tool call is a session-integrity problem no matter which files\n// changed. `hook-bypass` is NOT here: it's scoped below (a `push --no-verify` only skips\n// pre-push hooks ⇒ session hygiene, not a merge risk for this diff).\nconst PROCESS_PREFIXES = [\"pipe-sh\", \"tool-fabricated\", \"tool-malformed-args\"];\nconst isProcessFinding = (id: string): boolean =>\n PROCESS_PREFIXES.some((p) => id === p || id.startsWith(`${p}-`));\nconst isDestructive = (id: string): boolean => id.startsWith(\"destructive-\");\nconst isGitProcess = (id: string): boolean =>\n id.startsWith(\"force-push\") || id.startsWith(\"destructive-git\");\n\n/** True when the **destructive** git op in this command touches a diff file. Only the\n * destructive / history-rewriting invocation is judged — a benign sibling clause in the\n * same compound command (e.g. `git add <diff-file> && git reset --hard <ref>`) must NOT\n * make the unrelated reset look diff-relevant. Ref/history/worktree-wide ops\n * (`reset --hard <branch>`, `push -f`, `branch -D`) have no touched path ⇒ false ⇒\n * session hygiene, not a merge risk for this change. */\n/** True when every `--no-verify` in this command is on a `push` (no `commit --no-verify`).\n * A `push --no-verify` skips only the pre-push hooks (no committed content changes), so it\n * is session hygiene, not a merge risk for this diff. A `commit --no-verify` skipped the\n * pre-commit checks on committed content ⇒ NOT push-only ⇒ kept. */\nfunction hookBypassIsPushOnly(command: string): boolean {\n const noVerify = parseGitInvocations(command).filter(isNoVerify);\n return noVerify.length > 0 && noVerify.every((inv) => inv.subcommand === \"push\");\n}\n\nfunction gitTouchesDiff(command: string, files: readonly string[]): boolean {\n for (const inv of parseGitInvocations(command)) {\n if (!destroysGitData(inv) && !rewritesHistory(inv)) {\n continue; // a `git add`/`commit`/`checkout <branch>` clause is not the destructive op\n }\n if (touchedPaths(inv).some((p) => inDiff(p, files))) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Keep a finding under a diff scope:\n * - path-free integrity findings (hook-bypass, pipe-sh, fabricated/malformed) — always,\n * - a git process/destructive op only when its touched paths fall in the diff\n * (`reset --hard <branch>`/`push -f` touch nothing ⇒ dropped to the session card),\n * - a non-git destructive whose target is a repo path (not a `$TMP`/`.receipts` scratch),\n * - any other finding about a changed file.\n * Pure function of the finding + the recorded file list (+ the finding's own command,\n * itself re-derived from the transcript) ⇒ reproducible at verify.\n */\nexport function keepUnderDiff(\n f: Pick<Finding, \"id\" | \"title\"> & { filePath?: string; evidenceSpanId?: string },\n files: readonly string[],\n commandFor?: (f: Pick<Finding, \"id\" | \"evidenceSpanId\">) => string | undefined,\n locusFor?: (f: Pick<Finding, \"id\" | \"evidenceSpanId\">) => \"here\" | \"elsewhere\" | undefined,\n): boolean {\n // Cross-repo bleed (SPEC-0069 addendum, observed live): a session that also ran\n // `git commit --no-verify` in ANOTHER repo (`cd /other && git commit --no-verify`)\n // must not flag THIS repo's diff receipt. The locus is resolved by the caller from\n // transcript data only (span cwd + cd tracking), so the drop re-derives with no\n // git at verify time; \"elsewhere\" includes unresolvable-cd uncertainty (under-claim).\n if (locusFor?.(f) === \"elsewhere\") {\n return false;\n }\n if (isProcessFinding(f.id)) {\n return true;\n }\n // hook-bypass: a `push --no-verify` skips only pre-push hooks (no code change) ⇒ session\n // hygiene, dropped from the diff; a `commit --no-verify` (or an unresolvable command) is\n // kept — it may have skipped pre-commit checks on the committed change.\n if (f.id === \"hook-bypass\" || f.id.startsWith(\"hook-bypass-\")) {\n const cmd = commandFor?.(f);\n return cmd ? !hookBypassIsPushOnly(cmd) : true;\n }\n // git ref/history surgery is a merge risk only if it touched a changed file.\n if (isGitProcess(f.id)) {\n const cmd = commandFor?.(f);\n return cmd ? gitTouchesDiff(cmd, files) : false;\n }\n if (isDestructive(f.id)) {\n // scratch (`/tmp`) and the tool's own regenerable `.receipts/*.json` are both\n // out-of-scope for \"did this change do damage?\" — drop either from the diff.\n if (destructiveOutsideRepo(f.title) || regenerableReceiptRm(f.title)) {\n return false;\n }\n // a git destructive (`git reset --hard <branch>`, `git rm`) only counts when it hit\n // the diff; a non-git filesystem removal (`rm -rf src/old`) keeps the repo-path\n // behaviour. The command comes from the span resolver, falling back to the title's\n // clause (`Destructive op: <cmd>`), so this holds even without a resolver.\n const cmd =\n commandFor?.(f) ?? f.title.replace(/^Destructive op:\\s*/i, \"\").replace(/\\s*×\\d+\\s*$/, \"\");\n if (/(^|[\\s;&|])git\\s/.test(cmd)) {\n return gitTouchesDiff(cmd, files);\n }\n return true;\n }\n return f.filePath ? inDiff(f.filePath, files) : false;\n}\n\nexport interface ScopedData {\n derived: DerivedSummary;\n findings: FindingSet;\n}\n\n/**\n * Cost/tokens of just the generation turns that edited a file in the diff — \"what\n * producing *this change* cost\", versus the whole-session total. Each edit tool span\n * links to the assistant turn that emitted it via `parentSpanId`; we collect those\n * generations and sum their recorded/estimated cost + tokens. Pure function of\n * `derived.spans` (re-derived from the transcript) + `files` ⇒ reproducible at verify.\n *\n * Honest bound: a turn that edited a diff file *and* others is counted whole, and the\n * turn's cost includes its shared context overhead — so this is an upper estimate of the\n * diff's marginal cost, not a precise split. It is always ≤ the session total and, in a\n * session that produced several PRs, far below it.\n */\nexport function diffEffort(\n derived: DerivedSummary,\n files: readonly string[],\n): { cost: number; tokens: number; turns: number } {\n const editedGens = new Set<string>();\n for (const s of derived.spans) {\n if (s.kind !== \"tool\" || !s.parentSpanId || !isEditTool(s.name)) {\n continue;\n }\n const fp = filePathOf(s.input);\n if (fp && inDiff(fp, files)) {\n editedGens.add(s.parentSpanId);\n }\n }\n let cost = 0;\n let tokens = 0;\n for (const s of derived.spans) {\n if (s.kind === \"generation\" && editedGens.has(s.spanId)) {\n // recorded cost when present, else estimate from the turn's tokens + model (the\n // generation span's name is the model id) — the same basis as the session total.\n cost += s.cost || estimateCost(s.tokens, s.name);\n tokens += s.tokens?.total ?? 0;\n }\n }\n return { cost, tokens, turns: editedGens.size };\n}\n\n/**\n * Branch-narrowed effort (SPEC-0068 R4): in a session that produced several PRs, the\n * whole-session `diffEffort` counts every turn that ever touched a hot file (cli.ts,\n * README) — observed 26× inflation on a marathon session. When the session carries the\n * receipt branch's turns, the honest bound is branch turns ∩ diff-file edits. Returns\n * null when there is no slice or the slice has zero diff-file-editing turns (e.g.\n * Bash-only writes) — callers then keep the whole-session bound and must NOT record\n * `scope.branch`, so the receipt always states how its effort was computed. Shared by\n * produce (`receipts pr`) and verify (rederive) so the bytes match (L1).\n */\nexport function narrowEffort(\n sliceDerived: DerivedSummary | null,\n files: readonly string[],\n): { cost: number; tokens: number; turns: number } | null {\n if (!sliceDerived) {\n return null;\n }\n const eff = diffEffort(sliceDerived, files);\n return eff.turns > 0 ? eff : null;\n}\n\n/**\n * Apply a diff scope to derived evidence + findings. Scopes the risk-relevant\n * evidence (`filesChanged`, `destructiveOps`) to the change and computes the diff's own\n * cost (`diffCostUsd`/`diffTokens`/`diffTurns`); leaves whole-session effort metrics\n * (`edits`/`commands`/`reads`/total tokens/cost) intact for the footer. Deterministic;\n * uses only `files` so re-derivation reproduces it from `predicate.scope.files`.\n */\nexport function applyDiffScope(\n derived: DerivedSummary,\n findings: FindingSet,\n files: readonly string[],\n projectPath?: string,\n): ScopedData {\n // evidenceSpanId → the span's command/cwd, so a git process/destructive finding can\n // be attributed to the diff by its touched paths and its repo. Built from\n // `derived.spans` (re-derived from the transcript) so the scope stays a pure\n // function of (derived, findings, files, projectPath) — all transcript-derived.\n const spanCmd = new Map<string, string>();\n const spanCwd = new Map<string, string>();\n for (const s of derived.spans) {\n if (s.spanId) {\n spanCmd.set(s.spanId, commandOf(s.input));\n if (s.cwd) {\n spanCwd.set(s.spanId, s.cwd);\n }\n }\n }\n const cmdOf = (f: Pick<Finding, \"evidenceSpanId\">): string | undefined =>\n f.evidenceSpanId ? spanCmd.get(f.evidenceSpanId) : undefined;\n // Did this finding's command actually run in the session's own project?\n // `cwdAtFirstGit` tracks `cd` clauses inside compounds; outside or uncertain ⇒\n // \"elsewhere\" (another repo's process risk — drop from the diff receipt).\n const locusOf = (f: Pick<Finding, \"evidenceSpanId\">): \"here\" | \"elsewhere\" | undefined => {\n if (!projectPath || !f.evidenceSpanId) {\n return undefined;\n }\n const base = spanCwd.get(f.evidenceSpanId);\n const state = cwdAtFirstGit(spanCmd.get(f.evidenceSpanId) ?? \"\", base);\n if (state.kind === \"unknown\") {\n return \"elsewhere\";\n }\n const at = state.kind === \"known\" ? state.path : base;\n if (!at) {\n return undefined;\n }\n return at === projectPath || at.startsWith(`${projectPath}/`) ? \"here\" : \"elsewhere\";\n };\n const keep = (f: Finding): boolean => keepUnderDiff(f, files, cmdOf, locusOf);\n const scopedFindings: FindingSet = {\n main: findings.main.filter(keep),\n minor: findings.minor.filter(keep),\n };\n const filesChanged = derived.filesChanged.filter((fc) => inDiff(fc.path, files));\n const destructiveCount = derived.spans\n .filter((s) => s.destructive)\n .filter((s) => {\n const cmd = commandOf(s.input);\n const clause = destructiveMatch(cmd) ?? cmd;\n if (destructiveOutsideRepo(clause) || regenerableReceiptRm(clause)) {\n return false;\n }\n // git ref/history surgery that touched no changed file isn't this diff's damage.\n if (/(^|[\\s;&|])git\\s/.test(cmd)) {\n return gitTouchesDiff(cmd, files);\n }\n return true;\n }).length;\n const eff = diffEffort(derived, files);\n return {\n derived: {\n ...derived,\n filesChanged,\n destructiveCount,\n diffCostUsd: eff.cost,\n diffTokens: eff.tokens,\n diffTurns: eff.turns,\n },\n findings: scopedFindings,\n };\n}\n","/**\n * Which surface a finding belongs to. The **merge gate** (the PR comment) is about\n * the *merged artifact* — what a reviewer must check before merging. The **operator**\n * view (the `receipts` card and `receipts trends`) is about *how the agent worked* —\n * efficiency, cost, loops, retries. One source of truth, shared by the PR-comment\n * renderer and its standalone Action mirror.\n */\n\n/** Kinds that describe agent behaviour/efficiency, not the merged change. */\nconst OPERATOR_KINDS = new Set([\n \"loop\",\n \"bottleneck\",\n \"cost-concentration\",\n \"cache-opportunity\",\n \"model-downgrade\",\n]);\n\nexport type Surface = \"merge\" | \"operator\";\n\nexport function findingSurface(id: string): Surface {\n // `errcluster-<tool>` (\"X failed N× before succeeding\") is an efficiency signal.\n if (OPERATOR_KINDS.has(id) || id.startsWith(\"errcluster-\")) {\n return \"operator\";\n }\n return \"merge\";\n}\n\n/**\n * True when a destructive finding's target is a **scratch path** (a `$VAR`, `/tmp`,\n * `/var/folders`, or `~`), so it can't be a merge risk and shouldn't gate the PR.\n * Parsed from the finding title's clause (the `destructiveMatch` output, e.g.\n * `Destructive op: rm -rf \"$TMP\"`). Conservative: only returns true when **every**\n * `rm`/`rmdir` operand is clearly non-repo — `rm -rf ./src`, `rm -rf build`,\n * `git reset --hard`, `DROP TABLE` are all kept (return false).\n */\nexport function destructiveOutsideRepo(title: string): boolean {\n const clause = title.replace(/^Destructive op:\\s*/i, \"\").replace(/\\s*×\\d+\\s*$/, \"\");\n // Only filesystem removals carry a path target we can judge; git/SQL/infra\n // destructives act on the repo or an external system → never \"outside repo\".\n const m = clause.match(/^\\s*(rm|rmdir)\\b(.*)$/i);\n if (!m) {\n return false;\n }\n const operands = m[2].split(/\\s+/).filter((t) => t && !t.startsWith(\"-\"));\n if (operands.length === 0) {\n return false;\n }\n const scratch = (t: string): boolean => {\n const p = t.replace(/^[\"']|[\"']$/g, \"\");\n return (\n /\\$/.test(p) ||\n p.startsWith(\"/tmp\") ||\n p.startsWith(\"/var/folders\") ||\n p.startsWith(\"/private/var/folders\") ||\n p.startsWith(\"~\")\n );\n };\n return operands.every(scratch);\n}\n\n/**\n * True when a destructive finding deletes only the tool's **own regenerable Receipt\n * artifact** (`rm .receipts/<branch>.json`), e.g. when regenerating it against a\n * corrected base. Like {@link destructiveOutsideRepo} this is a scope-layer carve-out:\n * a `.receipts/*.json` is inside the repo but is rewritten by `receipts pr`, so it\n * isn't a merge risk in the change under review. Conservative: only `rm`/`rmdir` whose\n * **every** operand is a `.receipts/<name>.json` file — a mixed command\n * (`rm .receipts/x.json src/app.ts`) or the whole dir (`rm -rf .receipts`) returns\n * false and keeps flagging.\n */\nexport function regenerableReceiptRm(title: string): boolean {\n const clause = title.replace(/^Destructive op:\\s*/i, \"\").replace(/\\s*×\\d+\\s*$/, \"\");\n const m = clause.match(/^\\s*(rm|rmdir)\\b(.*)$/i);\n if (!m) {\n return false;\n }\n const operands = m[2].split(/\\s+/).filter((t) => t && !t.startsWith(\"-\"));\n if (operands.length === 0) {\n return false;\n }\n // a flat `.receipts/<name>.json` file (receipts are one-per-branch, not nested) —\n // the dir itself (`.receipts`, `.receipts/`) has no `.json` operand, so it's kept.\n const receiptJson = (t: string): boolean =>\n /(?:^|\\/)\\.receipts\\/[^/]+\\.json$/.test(t.replace(/^[\"']|[\"']$/g, \"\"));\n return operands.every(receiptJson);\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 { applyDiffScope, narrowEffort } 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 const eff = narrowEffort(slice ? deriveSpans(slice) : null, scope.files);\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 return { rederived: true, matches: canonicalize(committed) === canonicalize(fresh) };\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"],"mappings":"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","names":["spawnSync","spawnSync","base"]}
@@ -656,7 +656,7 @@ function uncachedCost(usage, model) {
656
656
  var EDIT_NAMES = /^(edit|multiedit|write|notebookedit|str_replace_editor|str_replace|apply_patch|search_replace|create_file|edit_file|insert_edit_into_file|replace_string_in_file|patch|write_file|create)$/i;
657
657
  var CREATE_NAMES = /^(write|create_file|write_file|create|notebookedit)$/i;
658
658
  var READ_NAMES = /^(read|read_file|cat|view|open_file|view_file|notebookread)$/i;
659
- var COMMAND_NAMES = /^(bash|shell|local_shell|run_command|run_terminal_cmd|run_in_terminal|execute_command|exec|terminal|command|powershell)$/i;
659
+ var COMMAND_NAMES = /^(bash|shell|local_shell|exec_command|run_command|run_terminal_cmd|run_in_terminal|execute_command|exec|terminal|command|powershell)$/i;
660
660
  var SEARCH_NAMES = /^(grep|glob|grep_search|file_search|codebase_search|semantic_search|search|find|ripgrep|project_scan|list_dir|ls)$/i;
661
661
  var TODO_NAMES = /^(todowrite|todo_write|taskupdate|taskcreate|update_todo)$/i;
662
662
  var WEB_NAMES = /^(websearch|webfetch|web_search|fetch|browser|navigate)$/i;
@@ -758,7 +758,15 @@ var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set([
758
758
  // Cursor
759
759
  ]);
760
760
  var FILE_READ_TOOLS = /* @__PURE__ */ new Set(["Read", "read_file", "cat"]);
761
- var CMD_TOOLS = /* @__PURE__ */ new Set(["Bash", "shell", "run_command", "execute_command", "run_terminal_cmd"]);
761
+ var CMD_TOOLS = /* @__PURE__ */ new Set([
762
+ "Bash",
763
+ "shell",
764
+ "exec_command",
765
+ // Codex 0.137
766
+ "run_command",
767
+ "execute_command",
768
+ "run_terminal_cmd"
769
+ ]);
762
770
  var DELETE_TOOLS = /* @__PURE__ */ new Set(["delete_file", "rm"]);
763
771
  function toolInputField(input, keys) {
764
772
  if (!input || typeof input !== "object") {
@@ -3808,15 +3816,58 @@ function mapUsage2(u2) {
3808
3816
  if (!u2) {
3809
3817
  return void 0;
3810
3818
  }
3819
+ const cached = u2.cached_input_tokens ?? 0;
3820
+ const reasoning = u2.reasoning_output_tokens ?? 0;
3811
3821
  return withTotal({
3812
- input: u2.input_tokens ?? 0,
3813
- output: u2.output_tokens ?? 0,
3814
- cacheRead: u2.cached_input_tokens ?? 0,
3822
+ input: Math.max(0, (u2.input_tokens ?? 0) - cached),
3823
+ output: Math.max(0, (u2.output_tokens ?? 0) - reasoning),
3824
+ cacheRead: cached,
3815
3825
  cacheWrite: 0,
3816
- reasoning: u2.reasoning_output_tokens ?? 0,
3826
+ reasoning,
3817
3827
  total: 0
3818
3828
  });
3819
3829
  }
3830
+ function relativize(p, cwd) {
3831
+ return cwd && p.startsWith(`${cwd}/`) ? p.slice(cwd.length + 1) : p;
3832
+ }
3833
+ function parseApplyPatch(input) {
3834
+ const files = [];
3835
+ let cur = null;
3836
+ const flush = () => {
3837
+ if (cur) {
3838
+ files.push({ path: cur.path, change: cur.change, patch: cur.body.join("\n") });
3839
+ }
3840
+ };
3841
+ for (const line of input.split("\n")) {
3842
+ const m = /^\*\*\* (Add|Update|Delete) File: (.+)$/.exec(line);
3843
+ if (m) {
3844
+ flush();
3845
+ const kind = m[1].toLowerCase();
3846
+ cur = {
3847
+ path: m[2].trim(),
3848
+ change: kind === "add" ? "add" : kind === "delete" ? "delete" : "update",
3849
+ body: []
3850
+ };
3851
+ continue;
3852
+ }
3853
+ if (/^\*\*\* (Begin|End) Patch/.test(line) || /^\*\*\* Move to:/.test(line)) {
3854
+ continue;
3855
+ }
3856
+ if (cur) {
3857
+ cur.body.push(line);
3858
+ }
3859
+ }
3860
+ flush();
3861
+ return files;
3862
+ }
3863
+ function editCalls(callId, files) {
3864
+ return files.map((f, k) => ({
3865
+ id: files.length > 1 ? `${callId}#${k}` : callId || void 0,
3866
+ name: "apply_patch",
3867
+ input: { file_path: f.path, patch: f.patch, change: f.change },
3868
+ status: "ok"
3869
+ }));
3870
+ }
3820
3871
  function unwrap(record) {
3821
3872
  for (const key of ["payload", "item", "response"]) {
3822
3873
  const inner = record[key];
@@ -3844,13 +3895,18 @@ async function parseFile(filePath, withMessages) {
3844
3895
  let model;
3845
3896
  let cwd;
3846
3897
  let firstUserText;
3898
+ let userPrompt;
3847
3899
  let startedAt;
3848
3900
  let endedAt;
3849
- let totalUsage = emptyUsage();
3901
+ let cumulativeUsage;
3902
+ let perMessageUsage = emptyUsage();
3903
+ let sawCumulative = false;
3850
3904
  let messageCount = 0;
3851
3905
  let toolCallCount = 0;
3852
3906
  const messages = [];
3853
3907
  const toolCallById = /* @__PURE__ */ new Map();
3908
+ const editMsgByCallId = /* @__PURE__ */ new Map();
3909
+ let lastAssistant;
3854
3910
  await readJsonl(filePath, (record) => {
3855
3911
  if (!record || typeof record !== "object") {
3856
3912
  return;
@@ -3863,48 +3919,111 @@ async function parseFile(filePath, withMessages) {
3863
3919
  }
3864
3920
  const item = unwrap(top);
3865
3921
  const type = String(item.type ?? top.type ?? "");
3922
+ const info = item.info;
3866
3923
  if (typeof item.model === "string") {
3867
3924
  model ??= item.model;
3868
3925
  }
3869
3926
  if (typeof item.cwd === "string") {
3870
3927
  cwd ??= item.cwd;
3871
3928
  }
3872
- const usage = mapUsage2(
3873
- item.usage ?? top.usage ?? top.info?.total_token_usage
3929
+ const cumulative = mapUsage2(
3930
+ info?.total_token_usage ?? top.info?.total_token_usage
3874
3931
  );
3875
- if (usage && usage.total > 0) {
3876
- totalUsage = addUsage(totalUsage, usage);
3932
+ if (cumulative && cumulative.total > 0) {
3933
+ cumulativeUsage = cumulative;
3934
+ sawCumulative = true;
3935
+ const delta = mapUsage2(info?.last_token_usage);
3936
+ if (delta && delta.total > 0 && lastAssistant) {
3937
+ lastAssistant.usage = addUsage(lastAssistant.usage ?? emptyUsage(), delta);
3938
+ lastAssistant.model ??= model;
3939
+ }
3940
+ } else {
3941
+ const perMsg = mapUsage2(item.usage ?? top.usage);
3942
+ if (perMsg && perMsg.total > 0) {
3943
+ perMessageUsage = addUsage(perMessageUsage, perMsg);
3944
+ if (lastAssistant && !lastAssistant.usage) {
3945
+ lastAssistant.usage = perMsg;
3946
+ lastAssistant.model ??= model;
3947
+ }
3948
+ }
3949
+ }
3950
+ if (type === "user_message" && typeof item.message === "string") {
3951
+ userPrompt ??= item.message;
3877
3952
  }
3878
3953
  if (type === "message") {
3879
- const role = item.role === "assistant" || item.role === "system" ? item.role : "user";
3954
+ const role = item.role === "assistant" ? "assistant" : item.role === "system" || item.role === "developer" ? "system" : "user";
3880
3955
  messageCount++;
3881
3956
  const text = extractText(item.content);
3882
3957
  if (role === "user") {
3883
3958
  firstUserText ??= text;
3884
3959
  }
3885
3960
  if (withMessages) {
3886
- messages.push({
3961
+ const msg = {
3887
3962
  role,
3888
3963
  timestamp: ts,
3889
3964
  text: text || void 0,
3890
- usage,
3891
3965
  finishReason: role === "assistant" ? normalizeFinishReason(item.done_reason ?? item.finish_reason ?? top.done_reason) : void 0
3892
- });
3966
+ };
3967
+ messages.push(msg);
3968
+ if (role === "assistant") {
3969
+ lastAssistant = msg;
3970
+ }
3893
3971
  }
3894
3972
  } else if (type === "function_call" || type === "tool_call") {
3895
3973
  toolCallCount++;
3896
3974
  const callId = String(item.call_id ?? item.id ?? "");
3975
+ let input = item.arguments ?? item.input;
3976
+ if (typeof input === "string" && /^\s*[[{]/.test(input)) {
3977
+ try {
3978
+ input = JSON.parse(input);
3979
+ } catch {
3980
+ }
3981
+ }
3982
+ const argRec = input && typeof input === "object" ? input : {};
3897
3983
  const call = {
3898
3984
  id: callId || void 0,
3899
3985
  name: String(item.name ?? "tool"),
3900
- input: item.arguments ?? item.input,
3986
+ input,
3987
+ cwd: typeof argRec.workdir === "string" ? argRec.workdir : void 0,
3901
3988
  status: "running"
3902
3989
  };
3903
3990
  if (callId) {
3904
3991
  toolCallById.set(callId, call);
3905
3992
  }
3906
3993
  if (withMessages) {
3907
- messages.push({ role: "assistant", timestamp: ts, toolCalls: [call] });
3994
+ const msg = { role: "assistant", timestamp: ts, toolCalls: [call] };
3995
+ messages.push(msg);
3996
+ lastAssistant = msg;
3997
+ }
3998
+ } else if (type === "custom_tool_call") {
3999
+ toolCallCount++;
4000
+ const callId = String(item.call_id ?? item.id ?? "");
4001
+ const name = String(item.name ?? "tool");
4002
+ if (withMessages) {
4003
+ const calls = name === "apply_patch" && typeof item.input === "string" ? editCalls(callId, parseApplyPatch(item.input)) : [{ id: callId || void 0, name, input: item.input, status: "ok" }];
4004
+ const msg = { role: "assistant", timestamp: ts, toolCalls: calls };
4005
+ messages.push(msg);
4006
+ lastAssistant = msg;
4007
+ if (name === "apply_patch" && callId) {
4008
+ editMsgByCallId.set(callId, msg);
4009
+ }
4010
+ }
4011
+ } else if (type === "patch_apply_end") {
4012
+ const callId = String(item.call_id ?? "");
4013
+ const success = item.success !== false;
4014
+ const changes = item.changes;
4015
+ const msg = editMsgByCallId.get(callId);
4016
+ if (msg) {
4017
+ if (!success) {
4018
+ msg.toolCalls = [];
4019
+ } else if (changes && Object.keys(changes).length > 0) {
4020
+ const files = Object.entries(changes).map(([p, ch]) => ({
4021
+ path: relativize(p, cwd),
4022
+ patch: String(ch?.unified_diff ?? ""),
4023
+ change: ch?.type === "add" ? "add" : ch?.type === "delete" ? "delete" : "update"
4024
+ }));
4025
+ msg.toolCalls = editCalls(callId, files);
4026
+ }
3908
4027
  }
3909
4028
  } else if (type === "function_call_output" || type === "tool_result") {
3910
4029
  const callId = String(item.call_id ?? item.id ?? "");
@@ -3915,6 +4034,7 @@ async function parseFile(filePath, withMessages) {
3915
4034
  }
3916
4035
  }
3917
4036
  });
4037
+ const totalUsage = sawCumulative ? cumulativeUsage ?? emptyUsage() : perMessageUsage;
3918
4038
  const totals = {
3919
4039
  tokens: totalUsage,
3920
4040
  durationMs: startedAt !== void 0 && endedAt !== void 0 ? endedAt - startedAt : void 0,
@@ -3925,7 +4045,7 @@ async function parseFile(filePath, withMessages) {
3925
4045
  id: filePath,
3926
4046
  source: "codex",
3927
4047
  provider: "codex",
3928
- title: firstUserText ? truncate(firstUserText) : void 0,
4048
+ title: userPrompt || firstUserText ? truncate(userPrompt ?? firstUserText) : void 0,
3929
4049
  model,
3930
4050
  projectPath: cwd,
3931
4051
  startedAt,
@@ -4600,4 +4720,4 @@ export {
4600
4720
  redact,
4601
4721
  redactReceipt
4602
4722
  };
4603
- //# sourceMappingURL=chunk-EYM5WETZ.js.map
4723
+ //# sourceMappingURL=chunk-DBQWQVZZ.js.map