altimate-receipts 0.3.2 → 0.5.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-UHI6BGLE.js";
10
+ } from "./chunk-2QTR3AF4.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-RQLUZ6FQ.js.map
262
+ //# sourceMappingURL=chunk-QGUQOQXO.js.map
@@ -18,7 +18,7 @@ import {
18
18
  redactReceipt,
19
19
  rewritesHistory,
20
20
  touchedPaths
21
- } from "./chunk-UHI6BGLE.js";
21
+ } from "./chunk-2QTR3AF4.js";
22
22
 
23
23
  // src/receipt/canonical.ts
24
24
  function canonicalize(value) {
@@ -302,6 +302,13 @@ function diffEffort(derived, files) {
302
302
  }
303
303
  return { cost, tokens, turns: editedGens.size };
304
304
  }
305
+ function narrowEffort(sliceDerived, files) {
306
+ if (!sliceDerived) {
307
+ return null;
308
+ }
309
+ const eff = diffEffort(sliceDerived, files);
310
+ return eff.turns > 0 ? eff : null;
311
+ }
305
312
  function applyDiffScope(derived, findings, files) {
306
313
  const spanCmd = /* @__PURE__ */ new Map();
307
314
  for (const s of derived.spans) {
@@ -400,6 +407,18 @@ async function rederiveFromTranscript(path, opts = {}) {
400
407
  const scoped = applyDiffScope(derived, findings, scope.files);
401
408
  derived = scoped.derived;
402
409
  findings = scoped.findings;
410
+ if (scope.branch) {
411
+ const slice = sliceByBranch(loaded, scope.branch);
412
+ const eff = narrowEffort(slice ? deriveSpans(slice) : null, scope.files);
413
+ if (eff) {
414
+ derived = {
415
+ ...derived,
416
+ diffCostUsd: eff.cost,
417
+ diffTokens: eff.tokens,
418
+ diffTurns: eff.turns
419
+ };
420
+ }
421
+ }
403
422
  }
404
423
  const receipt = await buildReceipt(session, derived, findings, { scope });
405
424
  return opts.redact ? redactReceipt(receipt) : receipt;
@@ -520,10 +539,11 @@ export {
520
539
  destructiveOutsideRepo,
521
540
  changedFiles,
522
541
  inDiff,
542
+ narrowEffort,
523
543
  applyDiffScope,
524
544
  branchesIn,
525
545
  sliceByBranch,
526
546
  rederiveFromTranscript,
527
547
  compareToTranscript
528
548
  };
529
- //# sourceMappingURL=chunk-SUAQDKUV.js.map
549
+ //# sourceMappingURL=chunk-WISVSYA7.js.map
@@ -0,0 +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 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): boolean {\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): ScopedData {\n // evidenceSpanId → the span's command, so a git process/destructive finding can be\n // attributed to the diff by its touched paths. Built from `derived.spans` (re-derived\n // from the transcript) so the scope stays a pure function of (derived, findings, files).\n const spanCmd = new Map<string, string>();\n for (const s of derived.spans) {\n if (s.spanId) {\n spanCmd.set(s.spanId, commandOf(s.input));\n }\n }\n const cmdOf = (f: Pick<Finding, \"evidenceSpanId\">): string | undefined =>\n f.evidenceSpanId ? spanCmd.get(f.evidenceSpanId) : undefined;\n const keep = (f: Finding): boolean => keepUnderDiff(f, files, cmdOf);\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);\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"]}