@tangle-network/agent-app 0.45.26 → 0.45.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant/index.d.ts +1 -0
- package/dist/assistant/index.js +5 -5
- package/dist/chat-routes/index.js +21 -21
- package/dist/chat-store/index.js +2 -2
- package/dist/{chunk-B2UEW62O.js → chunk-5ZTFZBS6.js} +4 -4
- package/dist/{chunk-AGCRCOQH.js → chunk-AWPK7ZDS.js} +1 -1
- package/dist/chunk-C3CAYGGQ.js +18 -0
- package/dist/chunk-C3CAYGGQ.js.map +1 -0
- package/dist/chunk-EMLGWEHV.js +33 -0
- package/dist/chunk-EMLGWEHV.js.map +1 -0
- package/dist/{chunk-XEA5EG6P.js → chunk-EPZYYWCT.js} +8 -30
- package/dist/chunk-EPZYYWCT.js.map +1 -0
- package/dist/{chunk-WD2B6HGY.js → chunk-K4RA5T3A.js} +11 -32
- package/dist/chunk-K4RA5T3A.js.map +1 -0
- package/dist/chunk-NEJ7OMQS.js +351 -0
- package/dist/chunk-NEJ7OMQS.js.map +1 -0
- package/dist/{chunk-7V2I3JGZ.js → chunk-OTLEYHOG.js} +50 -12
- package/dist/chunk-OTLEYHOG.js.map +1 -0
- package/dist/{chunk-RT5RKAFX.js → chunk-QGSVF6DZ.js} +8 -18
- package/dist/chunk-QGSVF6DZ.js.map +1 -0
- package/dist/{chunk-KOMP6NDW.js → chunk-WC43OQMR.js} +45 -34
- package/dist/chunk-WC43OQMR.js.map +1 -0
- package/dist/{chunk-N3G3FSM4.js → chunk-YXA3U4JZ.js} +9 -47
- package/dist/chunk-YXA3U4JZ.js.map +1 -0
- package/dist/design-canvas-react/index.js +4 -4
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/legibility/cli.js +14 -8
- package/dist/legibility/cli.js.map +1 -1
- package/dist/legibility/index.js +2 -1
- package/dist/peer-floors/cli.js +14 -8
- package/dist/peer-floors/cli.js.map +1 -1
- package/dist/preflight/cli.js +4 -2
- package/dist/preflight/cli.js.map +1 -1
- package/dist/{run-tEsZUhAf.d.ts → run-D80hoLKh.d.ts} +1 -1
- package/dist/sandbox/index.d.ts +53 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/signoff/cli.d.ts +2 -2
- package/dist/signoff/cli.js +6 -18
- package/dist/signoff/cli.js.map +1 -1
- package/dist/signoff/index.d.ts +4 -420
- package/dist/signoff/index.js +3 -55
- package/dist/signoff/proof-cli.js +11 -6
- package/dist/signoff/proof-cli.js.map +1 -1
- package/dist/signoff/proof.d.ts +70 -271
- package/dist/signoff/proof.js +3 -73
- package/dist/signoff/proof.js.map +1 -1
- package/dist/spend/cli.d.ts +1 -0
- package/dist/spend/cli.js +119 -0
- package/dist/spend/cli.js.map +1 -0
- package/dist/spend/index.d.ts +640 -0
- package/dist/spend/index.js +196 -0
- package/dist/spend/index.js.map +1 -0
- package/dist/stream/index.js +14 -14
- package/dist/teams/index.js +9 -9
- package/dist/teams/invitations-api.js +7 -7
- package/dist/theme-contract/cli.js +6 -2
- package/dist/theme-contract/cli.js.map +1 -1
- package/dist/theme-contract/index.js +2 -1
- package/dist/{types-U7Nz-txa.d.ts → types-Bd6uW9vQ.d.ts} +1 -1
- package/dist/web-react/index.d.ts +28 -40
- package/dist/web-react/index.js +11 -11
- package/package.json +7 -1
- package/dist/chunk-7V2I3JGZ.js.map +0 -1
- package/dist/chunk-KOMP6NDW.js.map +0 -1
- package/dist/chunk-N3G3FSM4.js.map +0 -1
- package/dist/chunk-RT5RKAFX.js.map +0 -1
- package/dist/chunk-WD2B6HGY.js.map +0 -1
- package/dist/chunk-XEA5EG6P.js.map +0 -1
- /package/dist/{chunk-B2UEW62O.js.map → chunk-5ZTFZBS6.js.map} +0 -0
- /package/dist/{chunk-AGCRCOQH.js.map → chunk-AWPK7ZDS.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/signoff/proof-git.ts","../src/signoff/proof-record.ts","../src/signoff/proof-attach.ts","../src/signoff/proof-verify.ts"],"sourcesContent":["/**\n * The git facts a sign-off proof is bound to, read straight from the object\n * database rather than reported by the process that ran the checks.\n *\n * Everything here is re-derivable by anyone holding the repository, which is\n * what makes the proof checkable: a verifier never trusts a field in the JSON,\n * it recomputes the same value with the same commands and compares.\n */\nimport { spawnSync } from 'node:child_process'\nimport { mkdtempSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\n\n/** A git invocation that exited non-zero, carrying stderr so the caller can act. */\nexport class SignoffGitError extends Error {\n readonly args: readonly string[]\n readonly status: number | null\n readonly stderr: string\n\n constructor(args: readonly string[], status: number | null, stderr: string) {\n super(`git ${args.join(' ')} exited ${status ?? 'null'}: ${stderr.trim()}`)\n this.name = 'SignoffGitError'\n this.args = args\n this.status = status\n this.stderr = stderr\n }\n}\n\nexport interface GitResult {\n readonly status: number | null\n readonly stdout: string\n readonly stderr: string\n}\n\n/**\n * Run git and hand back the raw result. `GIT_OPTIONAL_LOCKS=0` keeps a read\n * from touching the index while another agent works the same worktree — this\n * repo is shared by concurrent sessions.\n */\nexport function runGit(repoDir: string, args: readonly string[], options: { readonly input?: string; readonly env?: Readonly<Record<string, string>> } = {}): GitResult {\n const result = spawnSync('git', [...args], {\n cwd: repoDir,\n encoding: 'utf8',\n input: options.input,\n env: { ...process.env, GIT_OPTIONAL_LOCKS: '0', ...options.env },\n maxBuffer: 128 * 1024 * 1024,\n })\n if (result.error) throw new Error(`git ${args.join(' ')} could not run: ${result.error.message}`)\n return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' }\n}\n\n/** Run git, or throw. Trailing newline is stripped — every caller wants the value, not the line. */\nexport function gitText(repoDir: string, args: readonly string[], options: { readonly input?: string; readonly env?: Readonly<Record<string, string>> } = {}): string {\n const result = runGit(repoDir, args, options)\n if (result.status !== 0) throw new SignoffGitError(args, result.status, result.stderr)\n return result.stdout.replace(/\\n$/, '')\n}\n\n/**\n * Is `ancestor` reachable from `descendant`? A port rather than a direct call so\n * the verifier stays a pure function over facts a caller supplies.\n */\nexport type IsAncestorFn = (ancestor: string, descendant: string) => boolean\n\nexport function gitIsAncestor(repoDir: string): IsAncestorFn {\n return (ancestor, descendant) => {\n const result = runGit(repoDir, ['merge-base', '--is-ancestor', ancestor, descendant])\n if (result.status === 0) return true\n if (result.status === 1) return false\n throw new SignoffGitError(['merge-base', '--is-ancestor', ancestor, descendant], result.status, result.stderr)\n }\n}\n\nexport interface CommitFacts {\n /** 40-hex commit id. */\n readonly commit: string\n /** 40-hex id of the tree the COMMIT points at. */\n readonly commitTree: string\n readonly parents: readonly string[]\n /** Committer date, UTC ISO-8601 with a `Z` suffix. */\n readonly committedAt: string\n}\n\n/** Resolve a revision to the commit it names, failing loud on an unknown rev. */\nexport function resolveCommit(repoDir: string, rev: string): string {\n return gitText(repoDir, ['rev-parse', '--verify', `${rev}^{commit}`])\n}\n\nexport function readCommitFacts(repoDir: string, rev: string): CommitFacts {\n const commit = resolveCommit(repoDir, rev)\n const record = gitText(repoDir, ['show', '--no-patch', '--format=%T%n%P%n%cI', commit])\n const [tree, parents, committedAt] = record.split('\\n')\n if (tree === undefined || parents === undefined || committedAt === undefined) {\n throw new Error(`git show returned an unreadable record for ${commit}: ${JSON.stringify(record)}`)\n }\n return {\n commit,\n commitTree: tree,\n parents: parents.length === 0 ? [] : parents.split(' '),\n committedAt: new Date(committedAt).toISOString(),\n }\n}\n\n/**\n * Hash the tree the checks actually ran against, including uncommitted and\n * untracked (non-ignored) files.\n *\n * This is the field that makes drift detectable. A sign-off that ran over an\n * edited worktree produces a tree id no commit carries, so it cannot verify\n * against the commit it claims — which is the intended outcome, not a bug.\n *\n * Written through a throwaway index (`GIT_INDEX_FILE`) so a concurrent agent's\n * staged work in the real index is neither read nor disturbed. `git add -A`\n * honours `.gitignore`, so `node_modules` / `dist` stay out.\n */\nexport function computeWorktreeTree(repoDir: string): string {\n const scratch = mkdtempSync(join(tmpdir(), 'agent-app-signoff-index-'))\n const indexFile = join(scratch, 'index')\n try {\n const env = { GIT_INDEX_FILE: indexFile }\n gitText(repoDir, ['add', '-A', '--'], { env })\n return gitText(repoDir, ['write-tree'], { env })\n } finally {\n rmSync(scratch, { recursive: true, force: true })\n }\n}\n","/**\n * The sign-off proof record: what a local verification run produces so that a\n * merged commit can be interrogated later.\n *\n * ## Threat model — read this before trusting a proof\n *\n * A proof defends against **accident and drift**:\n * - a check that was never run, or ran and failed, being reported as green\n * - a proof produced over a different tree than the commit carries\n * - a proof copied from one commit onto another\n * - a field edited by hand after the fact\n * - a run whose peer versions or tool versions differ from what a reader assumes\n *\n * It does **not** defend against a malicious operator. The HMAC key is a local\n * file on the same machine that runs the checks, so anyone who can run a\n * sign-off can also mint a proof for checks that never ran. There is no secret\n * server, by requirement. What the seal buys is that a proof cannot be produced\n * or altered by someone WITHOUT that key, and that a proof cannot be silently\n * retargeted, which is the whole failure class an absent CI leaves open.\n *\n * Trust in the numbers themselves comes from the steps being real commands with\n * real exit codes and an output digest — not from cryptography.\n */\nimport { spawnSync } from 'node:child_process'\nimport { createHash, createHmac, timingSafeEqual } from 'node:crypto'\nimport { readFileSync } from 'node:fs'\nimport { hostname, userInfo } from 'node:os'\nimport { join } from 'node:path'\nimport { z } from 'zod'\nimport { computeWorktreeTree, readCommitFacts } from './proof-git'\nimport type { SignoffStepStatus } from './types'\n\n/** Bumped when the canonical body shape changes; a verifier refuses versions it does not know. */\nexport const SIGNOFF_PROOF_VERSION = 1\n\nconst isoString = z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/, 'must be a UTC ISO-8601 timestamp')\nconst sha1Hex = z.string().regex(/^[0-9a-f]{40}$/, 'must be a 40-hex git object id')\nconst sha256Hex = z.string().regex(/^[0-9a-f]{64}$/, 'must be a 64-hex sha256 digest')\n\n/**\n * The runner's step outcomes, restated as proof vocabulary.\n *\n * A status is carried alongside the exit code because they answer different\n * questions and only one of them is safe on its own: a step that was `skipped`,\n * `cancelled` or `blocked` never produced an exit code at all, and defaulting\n * that to `0` is precisely how a run that did not happen reads as a pass.\n *\n * The `satisfies` and the `Exclude` below pin this list to the runner's own\n * union in BOTH directions, so a status added there fails this file's typecheck\n * instead of quietly arriving as an unmodelled string.\n */\nexport const SIGNOFF_STEP_STATUSES = ['passed', 'failed', 'skipped', 'cancelled', 'blocked'] as const satisfies readonly SignoffStepStatus[]\ntype UnmodelledStatus = Exclude<SignoffStepStatus, (typeof SIGNOFF_STEP_STATUSES)[number]>\nconst _everyRunnerStatusIsModelled: UnmodelledStatus[] = []\nvoid _everyRunnerStatusIsModelled\n\nexport const signoffProofStepSchema = z.object({\n /** Stable id a repo's required-step table refers to (`typecheck`, `test`, `knip`, …). */\n id: z.string().min(1),\n command: z.string().min(1),\n cwd: z.string().min(1),\n /** How the runner judged the step. Only `passed` can satisfy a requirement. */\n status: z.enum(SIGNOFF_STEP_STATUSES),\n exitCode: z.number().int(),\n durationMs: z.number().int().nonnegative(),\n startedAt: isoString,\n /** sha256 of the step's combined stdout+stderr. Logs are not carried; the digest is. */\n outputSha256: sha256Hex,\n})\n\nconst signoffProofPeerSchema = z.object({\n name: z.string().min(1),\n /** `null` when the package is not resolvable on disk — recorded, never guessed. */\n version: z.string().min(1).nullable(),\n})\n\nconst signoffProofSubjectSchema = z.object({\n repo: z.string().min(1),\n commit: sha1Hex,\n /** Tree the checks actually ran against, including uncommitted work. */\n tree: sha1Hex,\n /** Tree the commit itself carries. Equal to `tree` on a clean sign-off. */\n commitTree: sha1Hex,\n parents: z.array(sha1Hex),\n committedAt: isoString,\n})\n\nexport const signoffProofBodySchema = z.object({\n proofVersion: z.number().int().positive(),\n subject: signoffProofSubjectSchema,\n signedAt: isoString,\n host: z.object({\n hostname: z.string().min(1),\n platform: z.string().min(1),\n arch: z.string().min(1),\n user: z.string().min(1),\n }),\n tooling: z.object({\n node: z.string().min(1),\n pnpm: z.string().min(1).nullable(),\n peers: z.array(signoffProofPeerSchema),\n }),\n /**\n * Real elapsed time for the whole run. Recorded separately from the steps\n * because the runner schedules them as wide as their dependencies allow, so\n * the sum of step durations is the SERIAL cost and would overstate this.\n */\n wallClockMs: z.number().int().nonnegative(),\n /** Seeds fed to anything non-deterministic, so a reader can reproduce the same run. */\n seeds: z.record(z.string(), z.union([z.string(), z.number()])),\n /** The step ids this run claims were required. The verifier holds the authoritative table. */\n declaredRequired: z.array(z.string().min(1)),\n steps: z.array(signoffProofStepSchema),\n verdict: z.enum(['pass', 'fail']),\n})\n\nexport const signoffProofSealSchema = z.object({\n algorithm: z.enum(['sha256', 'hmac-sha256']),\n /** sha256 over the canonical body. Chains the seal to every field, including the commit and tree. */\n bodySha256: sha256Hex,\n /** First 12 hex of sha256(key), so a reader can tell WHICH key sealed this. */\n keyId: z.string().regex(/^[0-9a-f]{12}$/).nullable(),\n mac: sha256Hex.nullable(),\n})\n\nexport const signoffProofSchema = z.object({\n body: signoffProofBodySchema,\n seal: signoffProofSealSchema,\n})\n\nexport type SignoffProofStep = z.infer<typeof signoffProofStepSchema>\nexport type SignoffProofPeer = z.infer<typeof signoffProofPeerSchema>\nexport type SignoffProofSubject = z.infer<typeof signoffProofSubjectSchema>\nexport type SignoffProofBody = z.infer<typeof signoffProofBodySchema>\nexport type SignoffProofSeal = z.infer<typeof signoffProofSealSchema>\nexport type SignoffProof = z.infer<typeof signoffProofSchema>\n\ntype CanonicalValue = string | number | boolean | null | readonly CanonicalValue[] | { readonly [key: string]: CanonicalValue }\n\n/**\n * Deterministic JSON: object keys sorted, array order preserved, no whitespace.\n *\n * The seal is a hash over this string, so two readers must produce byte-identical\n * bytes from the same record. `undefined` throws rather than vanishing — a field\n * that silently disappears is a field the hash stops covering.\n */\nexport function canonicalJson(value: CanonicalValue): string {\n if (value === null) return 'null'\n if (typeof value === 'string') return JSON.stringify(value)\n if (typeof value === 'boolean') return value ? 'true' : 'false'\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) throw new Error(`canonicalJson: ${String(value)} is not representable`)\n return JSON.stringify(value)\n }\n if (Array.isArray(value)) return `[${value.map((entry) => canonicalJson(entry)).join(',')}]`\n const record = value as { readonly [key: string]: CanonicalValue }\n const keys = Object.keys(record).sort()\n const fields = keys.map((key) => {\n const entry = record[key]\n if (entry === undefined) throw new Error(`canonicalJson: field ${JSON.stringify(key)} is undefined`)\n return `${JSON.stringify(key)}:${canonicalJson(entry)}`\n })\n return `{${fields.join(',')}}`\n}\n\nexport function canonicalizeProofBody(body: SignoffProofBody): string {\n return canonicalJson(body as unknown as CanonicalValue)\n}\n\nexport function hashProofBody(body: SignoffProofBody): string {\n return createHash('sha256').update(canonicalizeProofBody(body), 'utf8').digest('hex')\n}\n\n/** The digest a runner records for a step's combined output. Shared so both halves agree. */\nexport function hashStepOutput(output: string): string {\n return createHash('sha256').update(output, 'utf8').digest('hex')\n}\n\nexport function signoffKeyId(key: Uint8Array): string {\n return createHash('sha256').update(key).digest('hex').slice(0, 12)\n}\n\n/** Read the local sign-off key. Throws when absent — an unreadable key is never a silent downgrade to unsealed. */\nexport function readSignoffKey(path: string): Uint8Array {\n const raw = readFileSync(path)\n if (raw.byteLength < 16) throw new Error(`sign-off key at ${path} is ${raw.byteLength} bytes; at least 16 are required`)\n return new Uint8Array(raw)\n}\n\nexport function sealProof(body: SignoffProofBody, key?: Uint8Array): SignoffProof {\n const canonical = canonicalizeProofBody(body)\n const bodySha256 = createHash('sha256').update(canonical, 'utf8').digest('hex')\n if (key === undefined) {\n return { body, seal: { algorithm: 'sha256', bodySha256, keyId: null, mac: null } }\n }\n return {\n body,\n seal: {\n algorithm: 'hmac-sha256',\n bodySha256,\n keyId: signoffKeyId(key),\n mac: createHmac('sha256', key).update(canonical, 'utf8').digest('hex'),\n },\n }\n}\n\n/** Constant-time comparison of two hex digests of equal length. */\nexport function macMatches(expected: string, actual: string): boolean {\n if (expected.length !== actual.length) return false\n return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(actual, 'hex'))\n}\n\nexport interface ToolingFactsInput {\n readonly repoDir: string\n /** Package names whose resolved on-disk version belongs in the proof. */\n readonly peerNames: readonly string[]\n}\n\n/**\n * The versions that actually resolved on disk — the field CI's clean install\n * makes trustworthy and a warm local `node_modules` does not. A below-floor peer\n * is invisible to typecheck and to a green suite (it fails at the wire call), so\n * the proof records what was really there rather than what the manifest asks for.\n */\nexport function collectToolingFacts(input: ToolingFactsInput): SignoffProofBody['tooling'] {\n return {\n node: process.version,\n pnpm: readPnpmVersion(input.repoDir),\n peers: input.peerNames.map((name) => ({ name, version: readInstalledVersion(input.repoDir, name) })),\n }\n}\n\n/** `null` when pnpm is not on PATH — a missing version is recorded as missing, never as a guess. */\nfunction readPnpmVersion(repoDir: string): string | null {\n const result = spawnSync('pnpm', ['--version'], { cwd: repoDir, encoding: 'utf8' })\n if (result.error || result.status !== 0) return null\n return result.stdout.trim()\n}\n\n/** Resolved version from the consumer's own `node_modules`, or `null` when the package is not there. */\nexport function readInstalledVersion(repoDir: string, packageName: string): string | null {\n try {\n const manifest = JSON.parse(readFileSync(join(repoDir, 'node_modules', packageName, 'package.json'), 'utf8')) as { version?: unknown }\n return typeof manifest.version === 'string' ? manifest.version : null\n } catch {\n return null\n }\n}\n\n/** Every `@tangle-network/*` name the repo declares as a peer or a dependency. */\nexport function tangleDependencyNames(repoDir: string): readonly string[] {\n const manifest = JSON.parse(readFileSync(join(repoDir, 'package.json'), 'utf8')) as {\n dependencies?: Record<string, string>\n peerDependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n }\n const names = new Set<string>()\n for (const block of [manifest.dependencies, manifest.peerDependencies, manifest.devDependencies]) {\n for (const name of Object.keys(block ?? {})) {\n if (name.startsWith('@tangle-network/')) names.add(name)\n }\n }\n return [...names].sort()\n}\n\nexport interface BuildSignoffProofInput {\n readonly repoDir: string\n /** Repo identity the verifier's required-step table is keyed on. */\n readonly repo: string\n /** Revision the sign-off is for; defaults to `HEAD`. */\n readonly rev?: string\n readonly steps: readonly SignoffProofStep[]\n /** Measured elapsed time for the run. Required — it is not derivable from the steps. */\n readonly wallClockMs: number\n /** Step ids this run treated as required. Checked against the verifier's table. */\n readonly declaredRequired: readonly string[]\n readonly seeds: Readonly<Record<string, string | number>>\n readonly peerNames?: readonly string[]\n readonly key?: Uint8Array\n readonly now?: Date\n}\n\nexport function buildSignoffProof(input: BuildSignoffProofInput): SignoffProof {\n const facts = readCommitFacts(input.repoDir, input.rev ?? 'HEAD')\n const body: SignoffProofBody = {\n proofVersion: SIGNOFF_PROOF_VERSION,\n subject: {\n repo: input.repo,\n commit: facts.commit,\n tree: computeWorktreeTree(input.repoDir),\n commitTree: facts.commitTree,\n parents: [...facts.parents],\n committedAt: facts.committedAt,\n },\n signedAt: (input.now ?? new Date()).toISOString(),\n wallClockMs: Math.max(0, Math.round(input.wallClockMs)),\n host: { hostname: hostname(), platform: process.platform, arch: process.arch, user: userInfo().username },\n tooling: collectToolingFacts({ repoDir: input.repoDir, peerNames: input.peerNames ?? tangleDependencyNames(input.repoDir) }),\n seeds: { ...input.seeds },\n declaredRequired: [...input.declaredRequired],\n steps: input.steps.map((step) => ({ ...step })),\n verdict: input.steps.every((step) => step.status === 'passed' && step.exitCode === 0) ? 'pass' : 'fail',\n }\n return sealProof(signoffProofBodySchema.parse(body), input.key)\n}\n\n/** Parse an untrusted proof document, failing loud on any shape the verifier cannot reason about. */\nexport function parseSignoffProof(json: string): SignoffProof {\n return signoffProofSchema.parse(JSON.parse(json))\n}\n\nexport function serializeSignoffProof(proof: SignoffProof): string {\n return `${JSON.stringify(proof, null, 2)}\\n`\n}\n\nfunction formatDuration(ms: number): string {\n const seconds = Math.round(ms / 1000)\n if (seconds < 60) return `${seconds}s`\n return `${Math.floor(seconds / 60)}m${String(seconds % 60).padStart(2, '0')}s`\n}\n\n/**\n * The single line an operator pastes into a PR or a merge commit: how many steps\n * ran, how long it took, the seeds, and the verdict.\n */\nexport function formatSignoffSummary(proof: SignoffProof): string {\n const { body, seal } = proof\n const passed = body.steps.filter((step) => step.status === 'passed' && step.exitCode === 0).length\n const serialMs = body.steps.reduce((total, step) => total + step.durationMs, 0)\n // Wall clock is the number an operator is deciding on. The serial total rides\n // alongside it only when the schedule actually overlapped, so a parallel run\n // shows its saving instead of claiming one.\n const wall = serialMs > body.wallClockMs ? `${formatDuration(body.wallClockMs)} (serial ${formatDuration(serialMs)})` : formatDuration(body.wallClockMs)\n const seeds = Object.entries(body.seeds)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, value]) => `${name}=${value}`)\n .join(' ')\n const seal_ = seal.algorithm === 'hmac-sha256' ? `sealed ${seal.algorithm} key ${seal.keyId ?? 'unknown'}` : `unsealed sha256 ${seal.bodySha256.slice(0, 12)}`\n const dirty = body.subject.tree === body.subject.commitTree ? '' : ' DIRTY-TREE'\n return [\n `signoff ${body.verdict}${dirty}`,\n `${passed}/${body.steps.length} steps`,\n wall,\n `${body.subject.repo}@${body.subject.commit.slice(0, 9)} tree ${body.subject.tree.slice(0, 9)}`,\n seeds.length === 0 ? 'seeds none' : `seeds ${seeds}`,\n `node ${body.tooling.node} pnpm ${body.tooling.pnpm ?? 'unresolved'}`,\n seal_,\n body.signedAt,\n ].join(' · ')\n}\n","/**\n * Attaching a proof to the commit it verified.\n *\n * ## Why `git notes`, and what the two rejected carriers cost\n *\n * **A commit-message trailer** cannot work for the field that matters. The\n * trailer is part of the commit object, so writing it changes the SHA the proof\n * claims to cover — the proof would have to be produced before the commit it\n * names exists. Amending re-writes the SHA again, and GitHub's squash-merge\n * rebuilds the message from the PR body, so the trailer does not survive the one\n * transition it would need to.\n *\n * **A committed artifact directory** (`.signoff/<sha>.json`) changes the tree,\n * so the tree hash inside the proof can never cover the file carrying it. It\n * also lands in every diff, and it is exactly the \"file someone can forget\"\n * shape the requirement rules out — deleting it is a normal-looking change.\n *\n * **`git notes` wins because it is the only carrier that is addressed BY the\n * SHA without being part of it.** The note is written after the commit exists,\n * it changes neither the SHA nor the tree, and `git notes show <sha>` is a\n * definite yes/no for any commit anyone can name.\n *\n * ## The tradeoff `git notes` really carries, and how this module answers it\n *\n * Notes are attached to a commit id, and a rebase or a squash-merge produces a\n * NEW commit id, so the note does not follow by itself. Two mechanisms close\n * that gap, and both are needed:\n *\n * 1. `notes.rewriteRef` (see `SIGNOFF_NOTES_GIT_CONFIG`) makes local rebase and\n * amend copy the note forward automatically.\n * 2. `resolveSignoffProof` falls back to a **tree scan**: it reads every proof\n * under the notes ref and matches on the TREE the commit carries. A rebase\n * or a squash of one branch onto an unchanged base preserves the tree, so\n * the content that was verified is still provably the content that merged,\n * even though the SHA changed. The verifier reports which binding it used —\n * `exact` or `tree-equivalent` — and never conflates them.\n *\n * Notes are neither pushed nor fetched by default; `SIGNOFF_NOTES_GIT_CONFIG`\n * carries the refspecs that fix that, and a missing note is reported as\n * `not-found`, never as a pass.\n */\nimport { parseSignoffProof, type SignoffProof } from './proof-record'\nimport { gitText, readCommitFacts, resolveCommit, runGit, SignoffGitError } from './proof-git'\n\nexport const SIGNOFF_NOTES_REF = 'refs/notes/signoff'\n\n/**\n * The git configuration a repo needs for notes to travel. Emitted by the CLI\n * when a proof cannot be found, because the usual cause is an unconfigured repo\n * rather than an unsigned commit.\n */\nexport const SIGNOFF_NOTES_GIT_CONFIG: readonly string[] = [\n `git config --add remote.origin.fetch '+${SIGNOFF_NOTES_REF}:${SIGNOFF_NOTES_REF}'`,\n `git config --add remote.origin.push '${SIGNOFF_NOTES_REF}'`,\n `git config notes.rewriteRef '${SIGNOFF_NOTES_REF}'`,\n 'git config notes.rewrite.amend true',\n 'git config notes.rewrite.rebase true',\n]\n\nexport interface AttachSignoffProofInput {\n readonly repoDir: string\n readonly proof: SignoffProof\n /** Revision to annotate; defaults to the commit the proof names. */\n readonly rev?: string\n /** Replace an existing note. Off by default so a second sign-off cannot quietly overwrite the first. */\n readonly overwrite?: boolean\n}\n\nexport interface AttachedSignoffProof {\n readonly commit: string\n readonly ref: string\n}\n\nexport function attachSignoffProof(input: AttachSignoffProofInput): AttachedSignoffProof {\n const commit = resolveCommit(input.repoDir, input.rev ?? input.proof.body.subject.commit)\n const args = ['notes', `--ref=${SIGNOFF_NOTES_REF}`, 'add']\n if (input.overwrite === true) args.push('-f')\n args.push('-F', '-', commit)\n const result = runGit(input.repoDir, args, { input: `${JSON.stringify(input.proof, null, 2)}\\n` })\n if (result.status !== 0) throw new SignoffGitError(args, result.status, result.stderr)\n return { commit, ref: SIGNOFF_NOTES_REF }\n}\n\nexport type SignoffProofLookup =\n | { readonly found: true; readonly proof: SignoffProof; readonly commit: string; readonly binding: 'exact' | 'tree-equivalent' }\n | { readonly found: false; readonly commit: string }\n\n/** The note attached to exactly this commit, or `found: false`. Never searches. */\nexport function readSignoffProofNote(repoDir: string, rev: string): SignoffProofLookup {\n const commit = resolveCommit(repoDir, rev)\n const result = runGit(repoDir, ['notes', `--ref=${SIGNOFF_NOTES_REF}`, 'show', commit])\n if (result.status !== 0) return { found: false, commit }\n return { found: true, proof: parseSignoffProof(result.stdout), commit, binding: 'exact' }\n}\n\n/** Every proof stored under the notes ref, paired with the commit it annotates. */\nexport function listSignoffProofs(repoDir: string): readonly { readonly commit: string; readonly proof: SignoffProof }[] {\n const listed = runGit(repoDir, ['notes', `--ref=${SIGNOFF_NOTES_REF}`, 'list'])\n if (listed.status !== 0) return []\n const out: { commit: string; proof: SignoffProof }[] = []\n for (const line of listed.stdout.split('\\n')) {\n const [noteBlob, commit] = line.trim().split(/\\s+/)\n if (noteBlob === undefined || commit === undefined) continue\n out.push({ commit, proof: parseSignoffProof(gitText(repoDir, ['cat-file', 'blob', noteBlob])) })\n }\n return out\n}\n\n/**\n * Answer \"was this SHA signed off\" for any commit, including one produced by a\n * rebase or a squash-merge of the branch that was actually verified.\n *\n * Exact note first. Then the tree scan — a proof whose `commitTree` equals this\n * commit's tree verified byte-identical content, which is the strongest claim\n * available once the SHA has been rewritten. The binding is returned so a caller\n * can tell the two apart; nothing here treats them as the same thing.\n */\nexport function resolveSignoffProof(repoDir: string, rev: string): SignoffProofLookup {\n const direct = readSignoffProofNote(repoDir, rev)\n if (direct.found) return direct\n const facts = readCommitFacts(repoDir, rev)\n for (const entry of listSignoffProofs(repoDir)) {\n if (entry.proof.body.subject.commitTree === facts.commitTree) {\n return { found: true, proof: entry.proof, commit: facts.commit, binding: 'tree-equivalent' }\n }\n }\n return { found: false, commit: facts.commit }\n}\n","/**\n * `verify-proof` — the half a reader runs, on a machine that did not produce\n * the proof, to decide whether a commit was really signed off.\n *\n * The verifier never believes the document. Every claim it can re-derive, it\n * re-derives from git and compares; every claim it cannot re-derive is reported\n * as recorded, not as checked. The one field the proof is deliberately not\n * allowed to own is **which steps were required**: a run that declares its own\n * bar can declare an empty one, so the authoritative table lives here and a\n * proof declaring fewer requirements than its repo's table fails as\n * `lowered-bar`.\n */\nimport type { CommitFacts, IsAncestorFn } from './proof-git'\nimport { gitIsAncestor, readCommitFacts } from './proof-git'\nimport { readFileSync } from 'node:fs'\nimport { resolveSignoffProof, SIGNOFF_NOTES_GIT_CONFIG } from './proof-attach'\nimport { canonicalizeProofBody, hashProofBody, macMatches, parseSignoffProof, SIGNOFF_PROOF_VERSION, type SignoffProof } from './proof-record'\nimport { createHmac } from 'node:crypto'\n\n/**\n * The steps a repo's sign-off MUST cover, transcribed from each repo's CI job.\n * A runner is free to run more; it may never run fewer.\n *\n * agent-app .github/workflows/ci.yml\n * tax-agent .github/workflows/deploy.yml (the `ci` job)\n * legal-agent .github/workflows/deploy.yml (the `ci` job)\n */\nexport const SIGNOFF_REQUIRED_STEPS: Readonly<Record<string, readonly string[]>> = {\n 'agent-app': ['install', 'typecheck', 'test:gates', 'test', 'build', 'test:generated', 'knip'],\n 'tax-agent': ['install', 'peer-check', 'typecheck', 'test', 'toolkit-deps', 'toolkit-test', 'build', 'worker-startup'],\n 'legal-agent': ['install', 'peer-check', 'typegen', 'typecheck', 'test', 'build:check'],\n}\n\n/** Throws for a repo with no table — an unrecognised repo has no bar, and no bar is not a pass. */\nexport function requiredStepsFor(repo: string): readonly string[] {\n const steps = SIGNOFF_REQUIRED_STEPS[repo]\n if (steps === undefined) {\n throw new Error(`no required-step table for repo ${JSON.stringify(repo)}; known: ${Object.keys(SIGNOFF_REQUIRED_STEPS).sort().join(', ')}`)\n }\n return steps\n}\n\nexport type SignoffFailureCode =\n | 'unsupported-version'\n | 'body-tampered'\n | 'mac-missing'\n | 'mac-invalid'\n | 'unknown-repo'\n | 'repo-mismatch'\n | 'tree-mismatch'\n | 'dirty-worktree'\n | 'commit-unbound'\n | 'missing-required-step'\n | 'lowered-bar'\n | 'duplicate-step'\n | 'step-failed'\n | 'verdict-fail'\n | 'stale-proof'\n\nexport interface SignoffFailure {\n readonly code: SignoffFailureCode\n readonly detail: string\n}\n\n/** How the proof attaches to the commit that was asked about. */\nexport type SignoffCommitBinding =\n /** The proof names this exact commit. */\n | 'exact'\n /** A different commit id, but byte-identical content — a rebase or a squash of what was verified. */\n | 'tree-equivalent'\n /** Neither. The proof does not describe this commit. */\n | 'none'\n\nexport interface SignoffVerification {\n readonly ok: boolean\n readonly commitBinding: SignoffCommitBinding\n /** True only when a key was supplied AND the mac over the canonical body matched. */\n readonly macChecked: boolean\n readonly failures: readonly SignoffFailure[]\n readonly proof: SignoffProof\n readonly target: CommitFacts\n readonly requiredSteps: readonly string[]\n}\n\nexport interface VerifySignoffProofOptions {\n /** The commit the reader is asking about, read from git — never from the proof. */\n readonly target: CommitFacts\n /**\n * Reachability in the target's history. Required, because the clock cannot\n * answer staleness on its own: git records committer time to the SECOND, so a\n * proof and the commit it is being replayed onto routinely share a timestamp.\n * Ancestry is exact and clock-free.\n */\n readonly isAncestor: IsAncestorFn\n /** Local HMAC key. Omitted, the mac is reported unchecked rather than assumed good. */\n readonly key?: Uint8Array\n /** Overrides the built-in table. Supplying `[]` is a deliberate no-bar check, and says so. */\n readonly requiredSteps?: readonly string[]\n /** Repo identity the caller expects; a mismatch against the proof is a failure, not a rename. */\n readonly expectRepo?: string\n}\n\nexport function verifySignoffProof(proof: SignoffProof, options: VerifySignoffProofOptions): SignoffVerification {\n const failures: SignoffFailure[] = []\n const { body, seal } = proof\n const target = options.target\n\n if (body.proofVersion !== SIGNOFF_PROOF_VERSION) {\n failures.push({ code: 'unsupported-version', detail: `proof declares version ${body.proofVersion}; this verifier reads ${SIGNOFF_PROOF_VERSION}` })\n }\n\n const recomputed = hashProofBody(body)\n if (recomputed !== seal.bodySha256) {\n failures.push({ code: 'body-tampered', detail: `seal claims body sha256 ${seal.bodySha256}; the body hashes to ${recomputed}` })\n }\n\n let macChecked = false\n if (options.key !== undefined) {\n if (seal.mac === null) {\n failures.push({ code: 'mac-missing', detail: 'a key was supplied but the proof carries no mac; it was produced unsealed' })\n } else {\n const expected = createHmac('sha256', options.key).update(canonicalizeProofBody(body), 'utf8').digest('hex')\n if (macMatches(expected, seal.mac)) macChecked = true\n else failures.push({ code: 'mac-invalid', detail: `mac does not verify under the supplied key (proof keyId ${seal.keyId ?? 'none'})` })\n }\n }\n\n if (options.expectRepo !== undefined && options.expectRepo !== body.subject.repo) {\n failures.push({ code: 'repo-mismatch', detail: `proof is for repo ${body.subject.repo}; ${options.expectRepo} was requested` })\n }\n\n let requiredSteps: readonly string[] = options.requiredSteps ?? []\n if (options.requiredSteps === undefined) {\n const known = SIGNOFF_REQUIRED_STEPS[body.subject.repo]\n if (known === undefined) {\n failures.push({ code: 'unknown-repo', detail: `no required-step table for ${body.subject.repo}; a repo with no declared bar cannot be signed off` })\n } else {\n requiredSteps = known\n }\n }\n\n const seen = new Map<string, number>()\n for (const step of body.steps) seen.set(step.id, (seen.get(step.id) ?? 0) + 1)\n for (const [id, count] of seen) {\n if (count > 1) failures.push({ code: 'duplicate-step', detail: `step ${id} appears ${count} times; a repeated id makes coverage ambiguous` })\n }\n\n const missing = requiredSteps.filter((id) => !seen.has(id))\n if (missing.length > 0) {\n failures.push({ code: 'missing-required-step', detail: `required step(s) never ran: ${missing.join(', ')}` })\n }\n\n const declared = new Set(body.declaredRequired)\n const understated = requiredSteps.filter((id) => !declared.has(id))\n if (understated.length > 0) {\n failures.push({ code: 'lowered-bar', detail: `proof declares a smaller required set than ${body.subject.repo}'s table; missing: ${understated.join(', ')}` })\n }\n\n for (const step of body.steps) {\n if (step.status !== 'passed') {\n failures.push({ code: 'step-failed', detail: `step ${step.id} is ${step.status}, not passed (exit ${step.exitCode}, ${step.command})` })\n } else if (step.exitCode !== 0) {\n // An internally inconsistent record: the runner called it passed and the\n // process disagreed. Reported rather than resolved in either direction.\n failures.push({ code: 'step-failed', detail: `step ${step.id} claims status passed but exited ${step.exitCode} (${step.command})` })\n }\n }\n\n if (body.verdict !== 'pass') {\n failures.push({ code: 'verdict-fail', detail: `the run recorded verdict ${body.verdict}` })\n }\n\n if (body.subject.tree !== body.subject.commitTree) {\n failures.push({\n code: 'dirty-worktree',\n detail: `checks ran against tree ${body.subject.tree} while the commit carries ${body.subject.commitTree}; uncommitted work was in the tree`,\n })\n }\n\n if (body.subject.commitTree !== target.commitTree) {\n failures.push({ code: 'tree-mismatch', detail: `proof covers tree ${body.subject.commitTree}; ${target.commit} carries ${target.commitTree}` })\n }\n\n const commitBinding: SignoffCommitBinding =\n body.subject.commit === target.commit ? 'exact' : body.subject.commitTree === target.commitTree ? 'tree-equivalent' : 'none'\n if (commitBinding === 'none') {\n failures.push({ code: 'commit-unbound', detail: `proof names commit ${body.subject.commit}, which is neither ${target.commit} nor its content` })\n }\n\n // Staleness, checked two ways because neither is sufficient alone.\n //\n // The clock catches a proof back-dated relative to the commit it names. Its\n // resolution is git's, one second, so it is deliberately not the only check.\n const signedAt = Date.parse(body.signedAt)\n if (signedAt < Date.parse(body.subject.committedAt)) {\n failures.push({ code: 'stale-proof', detail: `signed at ${body.signedAt}, before the commit it names was written at ${body.subject.committedAt}` })\n }\n // Ancestry catches the case the clock cannot: a proof accepted on CONTENT\n // grounds for a LATER commit in the same history. A rebase or a squash\n // replaces the verified commit, so it is not reachable from the result — but a\n // revert restores an old tree on top of work nobody verified, and there the\n // proof's commit IS an ancestor. Same tree, different history, unverified\n // commits in between.\n if (commitBinding === 'tree-equivalent' && options.isAncestor(body.subject.commit, target.commit)) {\n failures.push({\n code: 'stale-proof',\n detail: `${body.subject.commit} is an ancestor of ${target.commit}; the tree matches only because later work was undone, and that work was never signed off`,\n })\n }\n\n return { ok: failures.length === 0, commitBinding, macChecked, failures, proof, target, requiredSteps }\n}\n\nexport interface VerifySignoffAtRevInput {\n readonly repoDir: string\n readonly rev: string\n readonly key?: Uint8Array\n readonly requiredSteps?: readonly string[]\n readonly expectRepo?: string\n}\n\nexport type SignoffLookupFailure = { readonly found: false; readonly commit: string; readonly hint: readonly string[] }\nexport type SignoffVerifyOutcome = ({ readonly found: true } & SignoffVerification) | SignoffLookupFailure\n\n/** Verify by revision: find the proof attached to that SHA (or to its content), then check it. */\nexport function verifySignoffAtRev(input: VerifySignoffAtRevInput): SignoffVerifyOutcome {\n const lookup = resolveSignoffProof(input.repoDir, input.rev)\n const target = readCommitFacts(input.repoDir, input.rev)\n if (!lookup.found) return { found: false, commit: lookup.commit, hint: SIGNOFF_NOTES_GIT_CONFIG }\n return {\n found: true,\n ...verifySignoffProof(lookup.proof, {\n target,\n isAncestor: gitIsAncestor(input.repoDir),\n key: input.key,\n requiredSteps: input.requiredSteps,\n expectRepo: input.expectRepo,\n }),\n }\n}\n\nexport interface VerifySignoffFileInput {\n readonly repoDir: string\n readonly file: string\n /**\n * The commit to check the file against. REQUIRED, and deliberately not\n * defaulted to the commit the proof names: a proof checked against its own\n * subject can never fail the commit binding, which is a check that reads as\n * strong and cannot catch anything. The reader always states what they are\n * asking about.\n */\n readonly rev: string\n readonly key?: Uint8Array\n readonly requiredSteps?: readonly string[]\n readonly expectRepo?: string\n}\n\n/** Verify a proof document on disk. The repo is still required — \"matches the tree it claims\" is not answerable without it. */\nexport function verifySignoffProofFile(input: VerifySignoffFileInput): SignoffVerification {\n const proof = parseSignoffProof(readFileSync(input.file, 'utf8'))\n const target = readCommitFacts(input.repoDir, input.rev)\n return verifySignoffProof(proof, {\n target,\n isAncestor: gitIsAncestor(input.repoDir),\n key: input.key,\n requiredSteps: input.requiredSteps,\n expectRepo: input.expectRepo,\n })\n}\n\n/** One line per failure, prefixed by its code, in the order the checks ran. */\nexport function formatSignoffVerification(result: SignoffVerification): string {\n const head = result.ok\n ? `VERIFIED ${result.proof.body.subject.repo}@${result.target.commit.slice(0, 9)} binding=${result.commitBinding} mac=${result.macChecked ? 'checked' : 'unchecked'}`\n : `REJECTED ${result.proof.body.subject.repo}@${result.target.commit.slice(0, 9)} binding=${result.commitBinding} (${result.failures.length} failure${result.failures.length === 1 ? '' : 's'})`\n return [head, ...result.failures.map((failure) => ` ${failure.code}: ${failure.detail}`)].join('\\n')\n}\n"],"mappings":"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","names":["spawnSync","join","spawnSync","join","readFileSync","createHmac","readFileSync"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/theme-contract/index.ts"],"sourcesContent":["/**\n * Exportable theme-token contract checker — the incident guard for the\n * invisible-popover class of bugs.\n *\n * The failure mode (tax-agent's transparent model dropdown; the whole\n * `bg-surface-container-*` family): a consumer app ships a component that\n * references a theme token — either as `var(--popover)` or as a Tailwind class\n * like `bg-surface-container-high` that the agent-app preset maps to\n * `hsl(var(--popover))` — but the app's OWN build never emits that custom\n * property (it forgot `import '@tangle-network/agent-app/styles'`, or dropped a\n * token in its local tokens.css). CSS resolves the missing var to nothing, the\n * surface paints transparent, and NOTHING errors. It ships invisible.\n *\n * `tests/theme/tokens-contract.test.ts` guards agent-app's OWN components. This\n * module lifts that walking logic into a function every CONSUMER app can run\n * against ITS OWN source in CI, comparing references to the tokens.css agent-app\n * ships plus any extra CSS the app defines.\n *\n * ── What each check covers (scope is deliberately honest) ────────────────────\n *\n * 1. var(--…) check — COMPLETE. Every `var(--name)` literal in the scanned\n * source (inline styles, `bg-[var(--name)]` arbitrary Tailwind values, CSS\n * template strings) is matched and compared against the defined token set.\n * This is exact: a `var(--x)` reference is unambiguous. It is a raw-text\n * scan (no AST), so a `var(--x)` written inside a comment or string literal\n * counts too — deliberate: it keeps the single-source logic identical to the\n * agent-app self-test, and a dangling `var(--x)` in a comment is a smell\n * worth surfacing. Suppress a deliberate one with `allowlist`.\n *\n * 2. Tailwind-utility check — INTENTIONALLY PARTIAL. Bare classes like\n * `bg-card` carry no `var(--)` and so are invisible to check 1; Tailwind\n * resolves them to `hsl(var(--card))` at build via the preset. Fully\n * resolving arbitrary Tailwind config is out of scope (it would mean\n * re-implementing Tailwind). Instead we check the SPECIFIC known-dangerous\n * families that have actually shipped invisible: the MD3 surface ladder\n * (`surface-container` / `-high` / `-highest`) and the `card` / `popover`\n * elevation pairs — exactly the utilities the agent-app tailwind-preset\n * registers onto elevation tokens (see src/theme/tailwind-preset.ts, the\n * source of truth for this mapping). The canvas/sequence aliases\n * (`--bg-input`, `--text-primary`, …) are consumed as `bg-[var(--…)]`\n * arbitrary values and so are already covered fully by check 1 — they need\n * no entry here.\n *\n * Node-only (reads the filesystem) → this lives in the `./theme-contract`\n * subpath, NOT `./theme`, which must stay browser-clean (it's in the\n * browser-safe manifest test).\n */\n\nimport { type Dirent, existsSync, readFileSync, readdirSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** Define options for scanning source directories and CSS token files in a theme contract */\nexport interface ThemeContractOptions {\n /** Consumer source directories to scan for token references (recursively). */\n srcDirs: string[]\n /**\n * Path to the base tokens.css whose `--name:` definitions are the ground\n * truth. Defaults to the tokens.css agent-app ships (`./styles`) — the set a\n * consumer gets from `import '@tangle-network/agent-app/styles'`.\n */\n tokensCss?: string\n /**\n * Additional CSS files whose `--name:` definitions also count as defined —\n * the app's own overrides/extensions layered on top of the base tokens.\n */\n extraTokensCss?: string[]\n /**\n * Token names (e.g. `--my-app-accent`) to treat as always-defined, suppressing\n * them from the missing list. For app-specific vars defined outside any CSS\n * the checker can see (injected at runtime, from a third-party stylesheet, …).\n */\n allowlist?: string[]\n}\n\n/** Describe a missing theme contract variable and where it was referenced */\nexport interface ThemeContractMiss {\n /** The undefined custom property, e.g. `--popover`. */\n varName: string\n /**\n * Where it was referenced: `path/to/file.tsx`, or\n * `path/to/file.tsx (via bg-surface-container-high)` when the reference is a\n * Tailwind utility that resolves to the token rather than a literal var().\n */\n referencedIn: string\n}\n\n/** Describe the result of validating a theme contract including success status and missing items */\nexport interface ThemeContractResult {\n ok: boolean\n missing: ThemeContractMiss[]\n}\n\n/** Source extensions scanned for token references. */\nconst SOURCE_RE = /\\.(ts|tsx|js|jsx|mjs|cjs)$/\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'coverage'])\n\n/**\n * Known-dangerous Tailwind utility families and the elevation token each\n * resolves to, mirroring src/theme/tailwind-preset.ts. Ordered longest-suffix\n * first so `surface-container-highest` is matched before `surface-container`.\n * The negative look-around in {@link buildUtilityRe} makes ordering belt-and-\n * suspenders rather than load-bearing.\n */\nconst DANGEROUS_UTILITIES: ReadonlyArray<{ suffix: string; varName: string }> = [\n { suffix: 'surface-container-highest', varName: '--secondary' },\n { suffix: 'surface-container-high', varName: '--popover' },\n { suffix: 'surface-container', varName: '--card' },\n { suffix: 'card-foreground', varName: '--card-foreground' },\n { suffix: 'popover-foreground', varName: '--popover-foreground' },\n { suffix: 'card', varName: '--card' },\n { suffix: 'popover', varName: '--popover' },\n]\n\n/** Tailwind color-utility prefixes that can carry a background/text/border color. */\nconst UTILITY_PREFIXES = 'bg|text|border|ring|fill|stroke'\n\n/**\n * Match a whole utility class for `suffix`, tolerant of variants (`hover:`,\n * `dark:`) and opacity (`/95`) but not of longer siblings: the trailing\n * `(?![\\w-])` stops `bg-surface-container` from matching inside\n * `bg-surface-container-high`, and `bg-card` from matching inside\n * `bg-card-foreground`.\n */\nfunction buildUtilityRe(suffix: string): RegExp {\n return new RegExp(`(?<![\\\\w-])(?:${UTILITY_PREFIXES})-${suffix}(?![\\\\w-])`, 'g')\n}\n\n/** Recursively collect scannable source files under a directory. */\nfunction walkSources(dir: string): string[] {\n let entries: Dirent[]\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n } catch {\n return []\n }\n return entries.flatMap((e) => {\n if (e.isDirectory()) return SKIP_DIRS.has(e.name) ? [] : walkSources(join(dir, e.name))\n return SOURCE_RE.test(e.name) && !e.name.endsWith('.d.ts') ? [join(dir, e.name)] : []\n })\n}\n\n/**\n * Every `--name:` DEFINITION across the given CSS files. A definition is\n * `--name:` at the start of a (trimmed) line; RHS references like\n * `hsl(var(--card))` are mid-line and are never counted as definitions.\n */\nfunction definedVars(cssFiles: string[]): Set<string> {\n const defs = new Set<string>()\n for (const file of cssFiles) {\n let css: string\n try {\n css = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n for (const m of css.matchAll(/^\\s*(--[a-z0-9-]+)\\s*:/gim)) if (m[1]) defs.add(m[1])\n }\n return defs\n}\n\n/**\n * Default tokens.css: the one agent-app ships as `./styles`. Resolved relative\n * to this module's URL, but tolerant of where the bundler lands the running\n * code — tsup code-splits shared logic into a chunk at the dist ROOT, so the\n * tokens.css sits one directory DIFFERENTLY depending on layout:\n * - source (src/theme-contract/index.ts) → ../theme/tokens.css (src/theme)\n * - split chunk (dist/contract-*.js) → ./theme/tokens.css (dist/theme)\n * - unsplit entry (dist/theme-contract/index.js) → ../theme/tokens.css\n * Probe both and return the one that exists; fall back to the first for a\n * sensible error path if neither is present.\n */\nfunction defaultTokensCss(): string {\n const candidates = ['../theme/tokens.css', './theme/tokens.css'].map((rel) =>\n fileURLToPath(new URL(rel, import.meta.url)),\n )\n return candidates.find((p) => existsSync(p)) ?? candidates[0]!\n}\n\n/**\n * Check that every theme token a consumer's source references is actually\n * defined in the CSS that consumer ships. Returns the full missing set; the\n * caller decides how to fail (the bin exits non-zero on any miss).\n */\nexport function checkThemeContract(opts: ThemeContractOptions): ThemeContractResult {\n const tokensCss = opts.tokensCss ?? defaultTokensCss()\n const defined = definedVars([tokensCss, ...(opts.extraTokensCss ?? [])])\n const allow = new Set(opts.allowlist ?? [])\n const isDefined = (name: string) => defined.has(name) || allow.has(name)\n\n const files = opts.srcDirs.flatMap(walkSources)\n const utilityMatchers = DANGEROUS_UTILITIES.map((u) => ({ ...u, re: buildUtilityRe(u.suffix) }))\n\n // Dedupe by varName (literal check) and by varName+utility (utility check),\n // keeping the FIRST referencing file — enough to locate the offender without\n // drowning the report when one token is referenced across many files.\n const seenVar = new Map<string, string>()\n const seenUtility = new Map<string, string>()\n\n for (const file of files) {\n let text: string\n try {\n text = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n const where = displayPath(file)\n\n // Check 1 — literal var(--…) references.\n for (const m of text.matchAll(/var\\(\\s*(--[a-z0-9-]+)/gi)) {\n const name = m[1]\n if (!name || isDefined(name) || seenVar.has(name)) continue\n seenVar.set(name, where)\n }\n\n // Check 2 — known-dangerous Tailwind utility classes.\n for (const u of utilityMatchers) {\n if (isDefined(u.varName)) continue\n const key = `${u.varName}::${u.suffix}`\n if (seenUtility.has(key)) continue\n u.re.lastIndex = 0\n if (u.re.test(text)) seenUtility.set(key, `${where} (via ${firstUtilityHit(text, u.suffix)})`)\n }\n }\n\n const missing: ThemeContractMiss[] = [\n ...[...seenVar].map(([varName, referencedIn]) => ({ varName, referencedIn })),\n ...[...seenUtility].map(([key, referencedIn]) => ({ varName: key.split('::')[0]!, referencedIn })),\n ]\n return { ok: missing.length === 0, missing }\n}\n\n/** The literal utility class (with prefix) first seen in `text` for `suffix`, for the report. */\nfunction firstUtilityHit(text: string, suffix: string): string {\n const m = buildUtilityRe(suffix).exec(text)\n return m?.[0] ?? `<utility>-${suffix}`\n}\n\n/** Path relative to cwd when it stays inside it, else the path as given — for readable reports. */\nfunction displayPath(file: string): string {\n const rel = relative(process.cwd(), file)\n return rel && !rel.startsWith('..') ? rel : file\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/signoff/config.ts","../src/signoff/workflow-pin.ts","../src/signoff/node-version.ts","../src/signoff/exec.ts","../src/signoff/schedule.ts","../src/signoff/seeds.ts","../src/signoff/store.ts","../src/signoff/workspace.ts","../src/signoff/run.ts","../src/signoff/report.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { z } from 'zod'\nimport type { LoadedSignoffConfig, SignoffConfig, SignoffStepSpec } from './types'\n\n/**\n * Where the step list comes from.\n *\n * It is declared by the repo, never hardcoded here, because the three workflows\n * this has to reproduce do not share a shape: tax-agent filters its install to\n * one workspace package and runs a Python toolkit suite; legal-agent runs a\n * react-router typegen and a Worker startup check; agent-app runs a named\n * incident-class gate, a generated-project test and knip. A checker that\n * hardcodes any one of those is wrong for the other two.\n *\n * Three sources, in order:\n * 1. `signoff.config.mjs` (or `.js`) — the full surface, including per-step\n * `env`, `needs` and shuffle specs.\n * 2. a `signoff` key in `package.json` — the same shape, JSON-only.\n * 3. derived from the repo's own `scripts`, so a repo gets a real gate before\n * anyone writes a config. The derivation is documented below and its origin\n * is stamped into the proof, because a derived run is a weaker claim than a\n * declared one.\n */\n\nexport const SIGNOFF_CONFIG_FILES: readonly string[] = ['signoff.config.mjs', 'signoff.config.js']\n\nconst shuffleSchema = z.object({\n runs: z.number().int().positive().optional(),\n seeds: z.array(z.number().int()).optional(),\n args: z.array(z.string()).optional(),\n})\n\nconst stepSchema = z.object({\n name: z.string().min(1),\n run: z.string().min(1),\n cwd: z.string().optional(),\n env: z.record(z.string(), z.string()).optional(),\n needs: z.array(z.string()).optional(),\n timeoutMs: z.number().int().positive().optional(),\n shuffle: z.union([z.boolean(), shuffleSchema]).optional(),\n})\n\nconst configSchema = z.object({\n install: z\n .object({\n run: z.string().min(1).optional(),\n storeDirFlag: z.string().nullable().optional(),\n storeEnv: z.string().nullable().optional(),\n cwd: z.string().optional(),\n timeoutMs: z.number().int().positive().optional(),\n env: z.record(z.string(), z.string()).optional(),\n })\n .optional(),\n steps: z.array(stepSchema).min(1),\n maxParallel: z.number().int().positive().optional(),\n env: z.record(z.string(), z.string()).optional(),\n nodeVersion: z.string().min(1).optional(),\n carryFiles: z.array(z.string()).optional(),\n cacheDir: z.string().optional(),\n storeGenerations: z.number().int().positive().optional(),\n})\n\nfunction describeIssues(error: z.ZodError, where: string): string {\n const lines = error.issues.map((issue) => ` ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n return `signoff: ${where} is not a valid config:\\n${lines.join('\\n')}`\n}\n\nexport function parseSignoffConfig(value: unknown, where: string): SignoffConfig {\n const result = configSchema.safeParse(value)\n if (!result.success) throw new Error(describeIssues(result.error, where))\n return result.data as SignoffConfig\n}\n\n/**\n * The script → step derivation, and the dependency edges it asserts.\n *\n * The edges are the speed lever, so each one is a claim about the repo:\n * typecheck, the suite, the build and knip are ASSUMED to read source and not\n * each other's output, so they run concurrently. Only `test:generated`\n * genuinely consumes a build artifact, so only it declares a dependency.\n *\n * **That assumption is a guess, and it is wrong for at least one real repo.**\n * agent-app's own suite copies `dist/` into a generated project while `tsup`\n * (`clean: true`) is deleting it, which fails loudly as \"dist/ not built\" and\n * quietly as a half-copied package. A repo whose suite reads build output MUST\n * declare `needs: ['build']` on it in a `signoff.config.mjs`; the derivation\n * cannot see that from a script name, which is one more reason a derived run is\n * a weaker claim than a declared one.\n *\n * `build:check` supersedes `build` when both exist (legal-agent's `build:check`\n * is `pnpm build && …`, so running both builds twice for one verdict).\n */\nconst DERIVED_STEPS: readonly {\n readonly script: string\n readonly name: string\n readonly shuffle?: boolean\n readonly supersedes?: string\n readonly needsBuild?: boolean\n}[] = [\n { script: 'peer-check', name: 'peer floors' },\n { script: 'typecheck', name: 'typecheck' },\n { script: 'test:gates', name: 'incident-class gates' },\n { script: 'test', name: 'unit tests', shuffle: true },\n { script: 'build', name: 'build' },\n { script: 'build:check', name: 'build + worker checks', supersedes: 'build' },\n { script: 'test:generated', name: 'generated projects', needsBuild: true },\n { script: 'knip', name: 'dead-surface (knip)' },\n]\n\nexport function deriveSignoffConfig(scripts: Readonly<Record<string, string>>): {\n readonly config: SignoffConfig\n readonly used: readonly string[]\n} {\n const present = DERIVED_STEPS.filter((candidate) => scripts[candidate.script] !== undefined)\n const superseded = new Set(present.map((candidate) => candidate.supersedes).filter((name): name is string => !!name))\n const kept = present.filter((candidate) => !superseded.has(candidate.script))\n const buildStep = kept.find((candidate) => candidate.script === 'build' || candidate.script === 'build:check')\n\n const steps: SignoffStepSpec[] = kept.map((candidate) => ({\n name: candidate.name,\n run: `pnpm run ${candidate.script}`,\n ...(candidate.shuffle ? { shuffle: true as const } : {}),\n ...(candidate.needsBuild && buildStep ? { needs: [buildStep.name] } : {}),\n }))\n\n if (steps.length === 0) {\n throw new Error(\n 'signoff: no config and no recognizable scripts. Add a `signoff.config.mjs` naming the steps ' +\n `this repo's CI runs, or a package.json \"signoff\" key. Recognized script names: ` +\n `${DERIVED_STEPS.map((candidate) => candidate.script).join(', ')}.`,\n )\n }\n return { config: { steps }, used: kept.map((candidate) => candidate.script) }\n}\n\nexport interface LoadSignoffConfigOptions {\n readonly repoRoot: string\n /** Explicit config path. Missing file is an error, never a silent fallback. */\n readonly configPath?: string\n}\n\nexport async function loadSignoffConfig(options: LoadSignoffConfigOptions): Promise<LoadedSignoffConfig> {\n const { repoRoot, configPath } = options\n\n if (configPath !== undefined) {\n const abs = resolve(repoRoot, configPath)\n if (!existsSync(abs)) throw new Error(`signoff: no config at ${abs}`)\n return { config: await importConfig(abs), origin: { kind: 'file', path: abs } }\n }\n\n for (const candidate of SIGNOFF_CONFIG_FILES) {\n const abs = join(repoRoot, candidate)\n if (existsSync(abs)) return { config: await importConfig(abs), origin: { kind: 'file', path: abs } }\n }\n\n const pkgPath = join(repoRoot, 'package.json')\n if (!existsSync(pkgPath)) {\n throw new Error(`signoff: ${repoRoot} has no package.json, no signoff.config.mjs, and nothing to derive from.`)\n }\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as {\n signoff?: unknown\n scripts?: Record<string, string>\n }\n if (pkg.signoff !== undefined) {\n return { config: parseSignoffConfig(pkg.signoff, `${pkgPath} \"signoff\"`), origin: { kind: 'package-json', path: pkgPath } }\n }\n\n const derived = deriveSignoffConfig(pkg.scripts ?? {})\n return { config: derived.config, origin: { kind: 'derived', path: pkgPath, scripts: derived.used } }\n}\n\nasync function importConfig(abs: string): Promise<SignoffConfig> {\n const mod: unknown = await import(pathToFileURL(abs).href)\n const value = (mod as { default?: unknown }).default\n if (value === undefined) throw new Error(`signoff: ${abs} must have a default export`)\n return parseSignoffConfig(value, abs)\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\n/**\n * Read the Node pin out of the workflows that gate a merge.\n *\n * This exists because of a measured false PASS. legal-agent `4c0d688` was red\n * in CI on two test files — `Cannot bundle Node.js built-in \"node:sqlite\"` —\n * and the sign-off gate signed it off. Same bytes, same lockfile, same\n * hermetic tree: on Node 22 both files fail, on Node 24 both pass, 100% of the\n * time. The runtime was the whole difference, and the gate never saw it because\n * it looked for a pin in exactly two places the fleet does not keep one.\n *\n * None of tax-agent, legal-agent or agent-app has a `.nvmrc`. All three pin\n * `node-version: 22` in the workflow that IS the merge gate. So the pin the\n * gate is replacing was sitting in the file it is replacing, unread.\n *\n * Scope is deliberate: only workflows that trigger on `pull_request`. Those are\n * the merge gate. agent-app's `publish.yml` runs Node 24.18.0 for its release\n * jobs and triggers on push, and reading it would put the gate in permanent\n * conflict with `ci.yml`'s Node 22.\n *\n * Nothing here guesses. An expression (`${{ matrix.node }}`) is not a version,\n * a `node-version-file` that does not exist is not a pin, and two merge-gate\n * workflows on different majors is a question this module cannot answer — each\n * one refuses and names the config key that settles it.\n */\n\nexport interface WorkflowNodePin {\n /** Repo-relative workflow path, for the proof. */\n readonly file: string\n /** The version as written: `22`, `24.18.0`, `v20`. */\n readonly value: string\n /** `node-version`, or the `node-version-file` path it was read through. */\n readonly via: string\n}\n\nconst WORKFLOW_DIR = join('.github', 'workflows')\n\n/** Everything after an unquoted ` #`, plus surrounding quotes, is not the value. */\nfunction scalarValue(raw: string): string {\n const withoutComment = raw.replace(/\\s+#.*$/, '').trim()\n const quoted = /^(['\"])(.*)\\1$/.exec(withoutComment)\n return (quoted?.[2] ?? withoutComment).trim()\n}\n\nfunction indentOf(line: string): number {\n return line.length - line.trimStart().length\n}\n\nfunction isBlank(line: string): boolean {\n const trimmed = line.trim()\n return trimmed.length === 0 || trimmed.startsWith('#')\n}\n\n/**\n * Does this workflow run on `pull_request`?\n *\n * Both spellings the fleet uses are handled: the block form every workflow here\n * writes, and the inline form (`on: [push, pull_request]`). `on` is quoted in\n * some repos because YAML 1.1 reads a bare `on` as the boolean true, so the\n * quoted keys are matched too.\n */\nexport function triggersOnPullRequest(source: string): boolean {\n const lines = source.split('\\n')\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index] as string\n const header = /^(?:on|\"on\"|'on')\\s*:(.*)$/.exec(line)\n if (!header) continue\n\n const inline = scalarValue(header[1] ?? '')\n if (inline.length > 0) {\n return inline\n .replace(/^\\[|\\]$/g, '')\n .split(',')\n .map((token) => token.trim())\n .includes('pull_request')\n }\n\n // Block form: the trigger names sit at the first nesting level under `on:`.\n // Anything deeper belongs to a trigger's own options (`branches`, `paths`).\n let nesting: number | null = null\n for (let cursor = index + 1; cursor < lines.length; cursor += 1) {\n const body = lines[cursor] as string\n if (isBlank(body)) continue\n const bodyIndent = indentOf(body)\n if (bodyIndent === 0) break\n if (nesting === null) nesting = bodyIndent\n if (bodyIndent !== nesting) continue\n const key = /^\\s*(?:-\\s*)?([A-Za-z_][\\w-]*)\\s*:?\\s*$/.exec(body)\n if (key?.[1] === 'pull_request') return true\n }\n return false\n }\n return false\n}\n\n/** Every Node pin an `actions/setup-node` step declares in one workflow. */\nfunction pinsInWorkflow(repoRoot: string, file: string, source: string): WorkflowNodePin[] {\n const pins: WorkflowNodePin[] = []\n for (const line of source.split('\\n')) {\n const match = /^\\s*(node-version|node-version-file)\\s*:\\s*(\\S.*)$/.exec(line)\n if (!match) continue\n const key = match[1] as string\n const value = scalarValue(match[2] as string)\n // A matrix expression is several runtimes, not one. Refusing to guess is\n // the point; `resolveWorkflowNodePin` turns an all-expression repo into a\n // named refusal rather than a silent \"unpinned\".\n if (value.includes('${{')) continue\n\n if (key === 'node-version') {\n pins.push({ file, value, via: 'node-version' })\n continue\n }\n\n const target = join(repoRoot, value)\n if (!existsSync(target)) {\n throw new Error(\n `signoff: ${file} reads its Node pin from \"${value}\" (node-version-file) and that file does not exist. ` +\n 'The workflow this gate replaces cannot itself run, so there is nothing to verify against.',\n )\n }\n const declared = readFileSync(target, 'utf8')\n .split('\\n')\n .map((entry) => entry.trim())\n .find((entry) => entry.length > 0 && !entry.startsWith('#'))\n if (declared !== undefined) pins.push({ file, value: declared, via: `node-version-file ${value}` })\n }\n return pins\n}\n\n/** Node pins declared by every workflow that gates a merge, in filename order. */\nexport function scanMergeGateNodePins(repoRoot: string): WorkflowNodePin[] {\n const dir = join(repoRoot, WORKFLOW_DIR)\n if (!existsSync(dir)) return []\n\n const pins: WorkflowNodePin[] = []\n const files = readdirSync(dir)\n .filter((name) => name.endsWith('.yml') || name.endsWith('.yaml'))\n .sort()\n for (const name of files) {\n const source = readFileSync(join(dir, name), 'utf8')\n if (!triggersOnPullRequest(source)) continue\n pins.push(...pinsInWorkflow(repoRoot, `${WORKFLOW_DIR}/${name}`, source))\n }\n return pins\n}\n\nexport interface ResolvedWorkflowPin {\n readonly major: number\n readonly declared: string\n readonly source: string\n}\n\n/**\n * One Node major for the whole merge gate, or a refusal that names why not.\n *\n * `majorOf` is injected rather than imported to keep the direction of the\n * dependency one-way: `node-version.ts` owns what a version string means, this\n * module owns where the string lives.\n */\nexport function resolveWorkflowNodePin(\n repoRoot: string,\n majorOf: (raw: string) => number | null,\n): ResolvedWorkflowPin | null {\n const pins = scanMergeGateNodePins(repoRoot)\n if (pins.length === 0) return null\n\n const byMajor = new Map<number, WorkflowNodePin[]>()\n for (const pin of pins) {\n const major = majorOf(pin.value)\n // An alias (`lts/*`) is a pin this module cannot resolve without a network\n // call — the same treatment `.nvmrc` gives one.\n if (major === null) continue\n const bucket = byMajor.get(major)\n if (bucket) bucket.push(pin)\n else byMajor.set(major, [pin])\n }\n\n if (byMajor.size === 0) return null\n if (byMajor.size > 1) {\n const detail = [...byMajor.values()]\n .flat()\n .map((pin) => ` ${pin.file} (${pin.via}): ${pin.value}`)\n .join('\\n')\n throw new Error(\n 'signoff: the workflows that gate a merge here pin different Node majors, so there is no single ' +\n `runtime to verify:\\n${detail}\\n` +\n 'Declare `nodeVersion` in the signoff config to say which one a sign-off means.',\n )\n }\n\n const [entry] = [...byMajor.entries()]\n if (entry === undefined) return null\n const [major, matched] = entry\n const first = matched[0] as WorkflowNodePin\n const files = [...new Set(matched.map((pin: WorkflowNodePin) => pin.file))].join(', ')\n return { major, declared: first.value, source: `${files} (${first.via})` }\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { resolveWorkflowNodePin } from './workflow-pin'\n\n/**\n * Refuse to sign off on a runtime the product does not ship.\n *\n * CI pins its Node version (`actions/setup-node` with `node-version: 22` in all\n * three fleet workflows). A developer's shell does not — this host runs 24. A\n * local gate that inherits whatever is on `PATH` therefore verifies a runtime\n * nobody deploys, and reports it as the runtime that ships. That is the same\n * failure shape as a number measured in a narrower context than it is\n * presented in, and it is not acceptable in the thing that replaces the merge\n * gate.\n *\n * So the pin is read from the repo and enforced, or it is absent and the report\n * says so. Three sources, in order:\n *\n * - `signoff.config.mjs`'s `nodeVersion` — the explicit declaration.\n * - `.nvmrc` — the pin a repo already keeps for humans.\n * - the `pull_request` workflows' `node-version` (`./workflow-pin`) — the pin\n * CI itself runs on.\n *\n * **The third source was added because the first two were empty on the entire\n * fleet, and that produced a false PASS.** legal-agent `4c0d688` failed CI on\n * `Cannot bundle Node.js built-in \"node:sqlite\"` and this gate signed it off.\n * Reproduced in one installed tree, same bytes: Node 22 fails both files, Node\n * 24 passes both, every time. No `.nvmrc` exists in tax-agent, legal-agent or\n * agent-app; all three pin `node-version: 22` in the workflow being replaced.\n * A gate that replaces a workflow has to read the runtime that workflow pins.\n *\n * A `.nvmrc` and a workflow that disagree is not resolved by preference — it is\n * a refusal, because it means the local gate and CI verify different runtimes,\n * which is the exact defect this module exists to prevent.\n *\n * **`engines.node` is deliberately NOT read.** It is a floor (`\">=18\"`), not a\n * pin, so treating it as one manufactures refusals on every version above the\n * floor — and an unsatisfiable gate does not stop bad work, it gets waived.\n */\n\nexport interface NodeVersionRequirement {\n /** The declared major, e.g. `22`. */\n readonly major: number\n readonly declared: string\n /** Where it came from, for the proof. */\n readonly source: string\n}\n\n/** Leading major from a pin like `22`, `v22.22.3`, `22.22`, `lts/iron` (none). */\nfunction majorOf(raw: string): number | null {\n const match = /^v?(\\d+)(?:\\.|$)/.exec(raw.trim())\n return match?.[1] === undefined ? null : Number.parseInt(match[1], 10)\n}\n\nexport function resolveNodeRequirement(\n repoRoot: string,\n configured?: string,\n): NodeVersionRequirement | null {\n if (configured !== undefined) {\n const major = majorOf(configured)\n if (major === null) {\n throw new Error(\n `signoff: nodeVersion \"${configured}\" does not start with a major version. ` +\n 'Declare a pin like \"22\" or \"22.22.3\".',\n )\n }\n return { major, declared: configured.trim(), source: 'signoff config `nodeVersion`' }\n }\n\n let fromNvmrc: NodeVersionRequirement | null = null\n const nvmrc = join(repoRoot, '.nvmrc')\n if (existsSync(nvmrc)) {\n const raw = readFileSync(nvmrc, 'utf8').trim()\n const major = majorOf(raw)\n // An alias (`lts/iron`) is a pin this module cannot resolve without a\n // network call, so it is reported as no requirement rather than guessed.\n if (major !== null) fromNvmrc = { major, declared: raw, source: '.nvmrc' }\n }\n\n const fromWorkflow = resolveWorkflowNodePin(repoRoot, majorOf)\n if (fromNvmrc && fromWorkflow && fromNvmrc.major !== fromWorkflow.major) {\n throw new Error(\n `signoff: .nvmrc pins Node ${fromNvmrc.declared} and ${fromWorkflow.source} pins ` +\n `${fromWorkflow.declared}. A sign-off that replaces CI cannot verify two runtimes, and picking ` +\n 'one silently would sign off a runtime the other half of the repo says is wrong. Make them agree, ' +\n 'or declare `nodeVersion` in the signoff config.',\n )\n }\n if (fromNvmrc) return fromNvmrc\n if (fromWorkflow) return { ...fromWorkflow, source: fromWorkflow.source }\n return null\n}\n\n/**\n * Throw when the running Node cannot stand in for the declared one.\n *\n * Major-only: a patch difference is not a different runtime, and demanding an\n * exact patch would refuse every host that has not just re-installed.\n */\nexport function assertNodeVersion(requirement: NodeVersionRequirement | null, running = process.version): void {\n if (!requirement) return\n const runningMajor = majorOf(running)\n if (runningMajor === requirement.major) return\n throw new Error(\n `signoff: this repo pins Node ${requirement.declared} (${requirement.source}) and you are running ${running}. ` +\n 'A sign-off that replaces CI has to verify the runtime the product ships, so this refuses rather than ' +\n `reporting a pass it did not earn. Switch with \\`nvm use ${requirement.major}\\`, or change the pin if the ` +\n 'product really has moved.',\n )\n}\n","import { spawn } from 'node:child_process'\n\n/**\n * The subprocess primitive every sign-off step runs through.\n *\n * Three properties the gate depends on, none of which `execSync` gives:\n *\n * 1. **Cancellable.** Under fail-fast, a build still running when typecheck\n * fails is killed rather than waited out — otherwise the \"faster than CI\"\n * claim is spent waiting for work whose verdict no longer matters.\n * 2. **Process-group kill.** Steps run through `sh -c`, so killing the shell\n * leaves `vitest`'s forks and `tsup`'s dts worker orphaned and holding CPU.\n * The child is spawned as a group leader and the whole group is signalled.\n * 3. **Bounded, non-silent capture.** Output is capped, and when the cap is hit\n * the elision is stated in the captured text. A gate that quietly drops the\n * middle of a failure log is a gate that hides the failure.\n */\n\nexport interface CommandResult {\n readonly command: string\n readonly cwd: string\n readonly exitCode: number\n readonly signal: string | null\n readonly durationMs: number\n readonly output: string\n readonly truncated: boolean\n readonly timedOut: boolean\n}\n\nexport interface RunCommandOptions {\n readonly command: string\n readonly cwd: string\n readonly env?: Readonly<Record<string, string | undefined>>\n readonly timeoutMs?: number\n readonly signal?: AbortSignal\n /** Retained bytes before elision. Default 2 MiB. */\n readonly maxOutputBytes?: number\n /** Grace between SIGTERM and SIGKILL. Default 5 s. */\n readonly killGraceMs?: number\n readonly onData?: (chunk: string) => void\n}\n\nconst DEFAULT_MAX_OUTPUT_BYTES = 2 * 1024 * 1024\nconst DEFAULT_KILL_GRACE_MS = 5_000\n/** Fraction of the budget kept from the head; the rest is the tail. The head\n * carries the command echo and the first error, the tail carries the summary. */\nconst HEAD_SHARE = 0.25\n\n/**\n * Ring-ish buffer that keeps a head window and a tail window, so both the first\n * error and the final summary survive a very long log.\n */\nclass BoundedOutput {\n private head = ''\n private tail = ''\n private total = 0\n private readonly headBudget: number\n private readonly tailBudget: number\n\n constructor(private readonly budget: number) {\n this.headBudget = Math.floor(budget * HEAD_SHARE)\n this.tailBudget = budget - this.headBudget\n }\n\n push(chunk: string): void {\n this.total += chunk.length\n if (this.head.length < this.headBudget) {\n const room = this.headBudget - this.head.length\n this.head += chunk.slice(0, room)\n chunk = chunk.slice(room)\n if (chunk.length === 0) return\n }\n this.tail = (this.tail + chunk).slice(-this.tailBudget)\n }\n\n get truncated(): boolean {\n return this.total > this.budget\n }\n\n text(): string {\n if (!this.truncated) return this.head + this.tail\n const elided = this.total - this.head.length - this.tail.length\n return `${this.head}\\n\\n[signoff] ${elided} bytes elided (output exceeded ${this.budget} bytes)\\n\\n${this.tail}`\n }\n}\n\n/** Signal a whole process group, tolerating the race where it already exited. */\nfunction killGroup(pid: number, signal: NodeJS.Signals): void {\n try {\n process.kill(-pid, signal)\n } catch (err) {\n // ESRCH means the group is already gone, which is the outcome we wanted.\n // Anything else is a real failure to signal and must not be swallowed.\n if ((err as NodeJS.ErrnoException).code !== 'ESRCH') throw err\n }\n}\n\n/**\n * Run one shell command to completion and report what happened.\n *\n * Never throws on a non-zero exit — a failing step is data, not an exception.\n * It throws only when the process could not be started at all.\n */\nexport function runCommand(options: RunCommandOptions): Promise<CommandResult> {\n const {\n command,\n cwd,\n env,\n timeoutMs,\n signal,\n maxOutputBytes = DEFAULT_MAX_OUTPUT_BYTES,\n killGraceMs = DEFAULT_KILL_GRACE_MS,\n onData,\n } = options\n\n return new Promise<CommandResult>((resolve, reject) => {\n const startedAt = Date.now()\n const buffer = new BoundedOutput(maxOutputBytes)\n let timedOut = false\n let killTimer: NodeJS.Timeout | undefined\n let graceTimer: NodeJS.Timeout | undefined\n\n const child = spawn(command, {\n cwd,\n env: env as NodeJS.ProcessEnv | undefined,\n shell: true,\n // Group leader: lets one signal reach `sh` and everything it spawned.\n detached: true,\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n\n const pid = child.pid\n const terminate = (): void => {\n if (pid === undefined || child.exitCode !== null || child.signalCode !== null) return\n killGroup(pid, 'SIGTERM')\n graceTimer = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) killGroup(pid, 'SIGKILL')\n }, killGraceMs)\n graceTimer.unref()\n }\n\n const onAbort = (): void => terminate()\n signal?.addEventListener('abort', onAbort, { once: true })\n\n if (timeoutMs !== undefined) {\n killTimer = setTimeout(() => {\n timedOut = true\n terminate()\n }, timeoutMs)\n killTimer.unref()\n }\n\n const collect = (chunk: Buffer): void => {\n const text = chunk.toString('utf8')\n buffer.push(text)\n onData?.(text)\n }\n child.stdout.on('data', collect)\n child.stderr.on('data', collect)\n\n const cleanup = (): void => {\n if (killTimer) clearTimeout(killTimer)\n if (graceTimer) clearTimeout(graceTimer)\n signal?.removeEventListener('abort', onAbort)\n }\n\n child.on('error', (err) => {\n cleanup()\n reject(new Error(`signoff: could not start \\`${command}\\` in ${cwd}: ${err.message}`))\n })\n\n child.on('close', (code, sig) => {\n cleanup()\n resolve({\n command,\n cwd,\n // A signalled process reports code `null`; 128+n is the shell's own\n // convention and keeps the field a number a caller can compare.\n exitCode: code ?? (sig === 'SIGKILL' ? 137 : 143),\n signal: sig,\n durationMs: Date.now() - startedAt,\n output: buffer.text(),\n truncated: buffer.truncated,\n timedOut,\n })\n })\n })\n}\n","/**\n * The dependency graph, and running it as wide as the graph allows.\n *\n * CI runs these steps in a line because a YAML `steps:` list is a line. Nothing\n * about the work requires that: typecheck, the suite, the build and knip all\n * read source and none reads another's output. On a 32-core host, running them\n * concurrently is free wall-clock, and it is the only reason a strictly larger\n * verification can still finish faster than the CI it replaces.\n *\n * The graph is declared per step (`needs`), validated here, and the schedule is\n * derived from it — so an omitted edge is a correctness bug the repo owns, and\n * a cycle is named rather than deadlocking.\n */\n\nexport interface GraphNode {\n readonly name: string\n readonly needs?: readonly string[]\n}\n\n/** Throws on a duplicate name, a dangling `needs`, or a cycle. */\nexport function validateGraph(nodes: readonly GraphNode[]): void {\n const seen = new Set<string>()\n for (const node of nodes) {\n if (seen.has(node.name)) throw new Error(`signoff: two steps are both named \"${node.name}\"; names must be unique`)\n seen.add(node.name)\n }\n for (const node of nodes) {\n for (const need of node.needs ?? []) {\n if (!seen.has(need)) {\n throw new Error(`signoff: step \"${node.name}\" needs \"${need}\", which is not a step in this config`)\n }\n }\n }\n\n const byName = new Map(nodes.map((node) => [node.name, node]))\n const state = new Map<string, 'visiting' | 'done'>()\n const walk = (name: string, path: readonly string[]): void => {\n const status = state.get(name)\n if (status === 'done') return\n if (status === 'visiting') {\n const cycle = [...path.slice(path.indexOf(name)), name].join(' -> ')\n throw new Error(`signoff: dependency cycle among steps: ${cycle}`)\n }\n state.set(name, 'visiting')\n for (const need of byName.get(name)?.needs ?? []) walk(need, [...path, name])\n state.set(name, 'done')\n }\n for (const node of nodes) walk(node.name, [])\n}\n\nexport type TaskStatus = 'passed' | 'failed' | 'skipped' | 'cancelled' | 'blocked'\n\nexport interface TaskOutcome<T> {\n readonly name: string\n readonly status: TaskStatus\n /** Present for anything that actually ran, including a cancelled task. */\n readonly value: T | null\n readonly startedAtMs: number | null\n readonly finishedAtMs: number | null\n}\n\nexport interface RunGraphOptions<TNode extends GraphNode, TValue> {\n readonly nodes: readonly TNode[]\n readonly maxParallel: number\n /** `false` stops scheduling after the first failure and kills what is running. */\n readonly keepGoing: boolean\n /** Runs one node. Resolves `{ ok }` for pass/fail; must not throw for a\n * failing command — a non-zero exit is data. */\n readonly run: (node: TNode, signal: AbortSignal) => Promise<{ readonly ok: boolean; readonly value: TValue }>\n /** Wall-clock origin, so outcomes are comparable across the whole run. */\n readonly now?: () => number\n}\n\n/**\n * Run the graph, honouring dependencies and the parallelism cap.\n *\n * Failure semantics, stated because they are the difference between a gate you\n * trust and one you argue with:\n * - fail-fast (default): nothing new is scheduled, everything in flight is\n * aborted and reported as `cancelled`, everything unstarted as `skipped`.\n * - `keepGoing`: independent work continues, but a step whose dependency\n * failed is reported `blocked`. It is never reported as passed, and never\n * silently omitted.\n */\nexport async function runGraph<TNode extends GraphNode, TValue>(\n options: RunGraphOptions<TNode, TValue>,\n): Promise<TaskOutcome<TValue>[]> {\n const { nodes, maxParallel, keepGoing, run, now = () => Date.now() } = options\n validateGraph(nodes)\n\n const origin = now()\n const outcomes = new Map<string, TaskOutcome<TValue>>()\n const pending = new Map(nodes.map((node) => [node.name, node]))\n const running = new Map<string, { readonly promise: Promise<void>; readonly controller: AbortController }>()\n let aborted = false\n\n const failedNames = new Set<string>()\n const passedNames = new Set<string>()\n\n const blockedBy = (node: TNode): boolean => (node.needs ?? []).some((need) => failedNames.has(need))\n const ready = (node: TNode): boolean => (node.needs ?? []).every((need) => passedNames.has(need))\n\n const settle = (name: string, outcome: TaskOutcome<TValue>): void => {\n outcomes.set(name, outcome)\n if (outcome.status === 'passed') passedNames.add(name)\n else failedNames.add(name)\n }\n\n const start = (node: TNode): void => {\n pending.delete(node.name)\n const controller = new AbortController()\n const startedAtMs = now() - origin\n const promise = run(node, controller.signal).then((result) => {\n const finishedAtMs = now() - origin\n const cancelled = controller.signal.aborted && !result.ok\n settle(node.name, {\n name: node.name,\n status: cancelled ? 'cancelled' : result.ok ? 'passed' : 'failed',\n value: result.value,\n startedAtMs,\n finishedAtMs,\n })\n running.delete(node.name)\n })\n running.set(node.name, { promise, controller })\n }\n\n for (;;) {\n if (!aborted) {\n // Drain everything the graph currently permits, up to the cap.\n for (const node of [...pending.values()]) {\n if (running.size >= maxParallel) break\n if (blockedBy(node)) {\n pending.delete(node.name)\n settle(node.name, { name: node.name, status: 'blocked', value: null, startedAtMs: null, finishedAtMs: null })\n continue\n }\n if (ready(node)) start(node)\n }\n }\n\n if (running.size === 0) {\n // Nothing running: either everything settled, or the rest is unreachable.\n if (pending.size === 0) break\n if (aborted) break\n // A pending node whose dependency neither passed nor failed cannot happen\n // after validateGraph, so anything left here is blocked or ready-next.\n const progressed = [...pending.values()].some((node) => ready(node) || blockedBy(node))\n if (!progressed) break\n continue\n }\n\n await Promise.race([...running.values()].map((entry) => entry.promise))\n\n if (!keepGoing && failedNames.size > 0 && !aborted) {\n aborted = true\n for (const entry of running.values()) entry.controller.abort()\n }\n }\n\n for (const node of pending.values()) {\n settle(node.name, {\n name: node.name,\n status: blockedBy(node) ? 'blocked' : 'skipped',\n value: null,\n startedAtMs: null,\n finishedAtMs: null,\n })\n }\n\n return nodes.map((node) => {\n const outcome = outcomes.get(node.name)\n if (!outcome) throw new Error(`signoff: step \"${node.name}\" produced no outcome — scheduler bug`)\n return outcome\n })\n}\n","import { createHash, randomInt } from 'node:crypto'\nimport type { SignoffShuffleSpec, SignoffStepSpec } from './types'\n\n/**\n * Suite-order randomization — the half of this gate that CI does not have.\n *\n * CI runs one arbitrary file order, so a scheduling-dependent failure is a coin\n * flip it happens to win or lose. The `node:sqlite` bundling failure that\n * started this was exactly that shape: it reproduced under CI's clean install\n * and worker sharding and not under a warm local run, and a single fixed order\n * could have missed it in either direction.\n *\n * Two rules make randomization useful rather than merely noisy:\n *\n * 1. **Every seed is recorded.** A shuffled failure that cannot be replayed is\n * a rumour. The report carries the base seed and every derived seed, and the\n * derivation is a pure function of `(base, step, index)` — so `--seed <base>`\n * reproduces the whole run, and a single seed replays one step.\n * 2. **Files are shuffled; tests inside a file are not.** File order is what\n * module-graph and worker-scheduling failures depend on. Shuffling the tests\n * within a file mostly finds intentional ordering in a `describe` block,\n * which is a false alarm, and a gate that cries wolf gets waived.\n */\n\n/**\n * Vitest file-order shuffle. `{seed}` is substituted per attempt.\n *\n * **No `--` separator, and that is measured, not assumed.** The habit is to\n * write `pnpm run test -- --flag`, and it silently breaks this: pnpm forwards\n * script arguments verbatim without needing the separator, so the `--` reaches\n * vitest, whose CLI treats it as end-of-options and drops everything after it.\n * Measured on this repo, four test files, `--reporter=verbose`:\n *\n * | invocation | seed 1 order | seed 2 order |\n * |---|---|---|\n * | `vitest run -- --sequence.shuffle.files=true --sequence.seed=N` | schedule, store, seeds, config | schedule, store, seeds, config |\n * | `vitest run --sequence.shuffle.files=true --sequence.seed=N` | schedule, config, store, seeds | seeds, schedule, config, store |\n *\n * The first row is the failure this gate exists to prevent, applied to itself:\n * a run that reports \"2 orders, seeds recorded\" while running one fixed order\n * twice. If a runner ever does need a separator, its config declares its own\n * `shuffle.args`.\n */\nexport const DEFAULT_SHUFFLE_ARGS: readonly string[] = [\n '--sequence.shuffle.files=true',\n '--sequence.seed={seed}',\n]\n\nexport const DEFAULT_SHUFFLE_RUNS = 2\n\n/** A fresh base seed. Random, then recorded — never a fixed constant, or the\n * \"randomized\" order is one more fixed order. */\nexport function newSeedBase(): number {\n return randomInt(0, 2 ** 31 - 1)\n}\n\n/**\n * Derive a step's Nth seed from the base.\n *\n * A hash rather than `base + n`: adjacent seeds produce correlated orders in\n * some runners, and the point is independent samples of the order space.\n */\nexport function deriveSeed(base: number, stepName: string, index: number): number {\n const digest = createHash('sha256').update(`${base}:${stepName}:${index}`).digest()\n return digest.readUInt32BE(0) % 2 ** 31\n}\n\n/**\n * Refuse a shuffled step whose command cannot receive the appended flags.\n *\n * pnpm only forwards extra arguments to a script through `run`, `exec` or\n * `dlx`. The shorthand form puts pnpm's own option parser in front of them, and\n * what happens next is a version lottery. Measured on this host, appending\n * `--sequence.shuffle.files=true --sequence.seed=7` to a script that prints its\n * `process.argv`:\n *\n * | command | pnpm 9.15.9 | pnpm 10.22.0 |\n * |---|---|---|\n * | `pnpm run t <flags>` | forwarded, exit 0 | forwarded, exit 0 |\n * | `pnpm exec node probe.mjs <flags>` | forwarded, exit 0 | forwarded, exit 0 |\n * | `pnpm t <flags>` | `Unknown options`, exit 1 | exit 254, script never ran |\n * | `pnpm --filter web t <flags>` | `Unknown options`, exit 1 | **exit 0, script never ran** |\n *\n * That last cell is the reason this is a refusal and not a note. tax-agent's CI\n * runs `pnpm --filter web test`, and a config that copied the line verbatim\n * would, on pnpm 10, report a green \"unit tests\" step that executed zero tests.\n * A gate reporting safety it did not provide is the exact failure this module\n * exists to prevent, and the shorthand makes it silent.\n *\n * This is a static approximation — it checks the invocation SHAPE, not what the\n * runner received — so it is deliberately narrow: it fires only on commands\n * that invoke pnpm, and only on steps that get arguments appended.\n */\nexport function assertShuffleArgsReachTheRunner(steps: readonly SignoffStepSpec[]): void {\n for (const step of steps) {\n if (!normalizeShuffle(step.shuffle)) continue\n const tokens = step.run.split(/\\s+/).filter((token) => token.length > 0)\n const pnpmAt = tokens.findIndex((token) => token === 'pnpm' || token.endsWith('/pnpm'))\n if (pnpmAt === -1) continue\n if (tokens.slice(pnpmAt + 1).some((token) => token === 'run' || token === 'exec' || token === 'dlx')) continue\n throw new Error(\n `signoff: step \"${step.name}\" runs \\`${step.run}\\` and is shuffled, but pnpm only forwards appended ` +\n 'arguments to a script through `run`, `exec` or `dlx`. In the shorthand form pnpm 9 errors and pnpm 10 ' +\n 'exits 0 having run nothing, which would report a passing suite that never executed. ' +\n `Write it as \\`${step.run.replace(/\\s(\\S+)$/, ' run $1')}\\`.`,\n )\n }\n}\n\nexport interface StepAttemptPlan {\n readonly command: string\n readonly seed: number | null\n}\n\nfunction normalizeShuffle(shuffle: boolean | SignoffShuffleSpec | undefined): SignoffShuffleSpec | null {\n if (shuffle === undefined || shuffle === false) return null\n return shuffle === true ? {} : shuffle\n}\n\n/**\n * Expand one step into the commands that will actually run.\n *\n * An unshuffled step is one attempt with no seed. A shuffled step is one\n * attempt per seed, each with the seed substituted into the appended arguments.\n */\nexport function planAttempts(\n step: SignoffStepSpec,\n seedBase: number,\n overrideRuns?: number,\n): StepAttemptPlan[] {\n const spec = normalizeShuffle(step.shuffle)\n if (!spec) return [{ command: step.run, seed: null }]\n\n const args = spec.args ?? DEFAULT_SHUFFLE_ARGS\n const seeds =\n spec.seeds && spec.seeds.length > 0\n ? [...spec.seeds]\n : Array.from({ length: overrideRuns ?? spec.runs ?? DEFAULT_SHUFFLE_RUNS }, (_unused, index) =>\n deriveSeed(seedBase, step.name, index),\n )\n\n return seeds.map((seed) => ({\n command: `${step.run} ${args.map((arg) => arg.replaceAll('{seed}', String(seed))).join(' ')}`,\n seed,\n }))\n}\n","import { createHash } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, utimesSync, writeFileSync } from 'node:fs'\nimport { join, relative, sep } from 'node:path'\n\n/**\n * The pristine package store, cached on the lockfile so speed is not paid for\n * with a stale module graph.\n *\n * The whole mechanical difference between \"green locally\" and \"red in CI\" is\n * that CI installs `--frozen-lockfile` into an isolated store with no\n * `node_modules` and no framework cache, and a local run reuses both. Only one\n * of those two is safe to keep:\n *\n * - `node_modules` and any build/framework cache are **never** reused. They are\n * what masked the failure this gate exists to catch, and they are recreated\n * for every run inside a fresh `git worktree`.\n * - The **store** is reused, keyed on the bytes that decide what gets installed.\n * A pnpm store is content-addressed: every entry is named by the hash of what\n * is in it, so a reused entry cannot be a different version of a package than\n * the lockfile asked for. Reusing it skips downloads, not resolution.\n *\n * The key covers every manifest in the tree — lockfile, workspace file, every\n * `package.json`, `.npmrc`. Change any of them and the key changes, so the next\n * run installs into an empty store and pays the honest cold cost. Generations\n * are kept (default 4) rather than one, so moving between a branch and `main`\n * finds both warm instead of thrashing.\n */\n\n/** Basenames whose bytes decide what an install resolves to. */\nexport const MANIFEST_FILES: readonly string[] = [\n 'pnpm-lock.yaml',\n 'pnpm-workspace.yaml',\n 'package.json',\n '.npmrc',\n '.nvmrc',\n 'package-lock.json',\n 'npm-shrinkwrap.json',\n 'yarn.lock',\n]\n\n/** Never descended into when hunting manifests. */\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.wrangler', '.react-router'])\n\nexport interface StoreResolution {\n readonly storeDir: string\n readonly cacheKey: string\n /** True when a store for this key already had content. */\n readonly hit: boolean\n /** Repo-relative manifest paths, sorted — the inputs to `cacheKey`. */\n readonly keyedOn: readonly string[]\n /** Store directories removed by the generation cap. */\n readonly pruned: readonly string[]\n}\n\nexport interface ResolveStoreOptions {\n /** The clean tree whose manifests are hashed. */\n readonly treePath: string\n readonly cacheDir: string\n /** Generations to keep. Default 4. */\n readonly generations?: number\n}\n\nfunction collectManifests(dir: string, root: string, out: string[]): void {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue\n collectManifests(join(dir, entry.name), root, out)\n } else if (MANIFEST_FILES.includes(entry.name)) {\n out.push(relative(root, join(dir, entry.name)).split(sep).join('/'))\n }\n }\n}\n\n/** Every manifest in the tree, repo-relative and sorted. */\nexport function manifestFiles(treePath: string): string[] {\n const found: string[] = []\n collectManifests(treePath, treePath, found)\n return found.sort()\n}\n\n/** sha256 over each manifest's path and content — order-independent by sorting. */\nexport function manifestCacheKey(treePath: string, files: readonly string[]): string {\n const hash = createHash('sha256')\n for (const rel of files) {\n hash.update(rel)\n hash.update('\\0')\n hash.update(createHash('sha256').update(readFileSync(join(treePath, rel))).digest('hex'))\n hash.update('\\n')\n }\n return hash.digest('hex')\n}\n\n/**\n * Prune all but the `keep` most recently used store generations.\n *\n * Recency is the directory's mtime, stamped on every use, so the store a run\n * just used is never the one evicted.\n */\nfunction pruneStores(storesRoot: string, keep: number): string[] {\n if (!existsSync(storesRoot)) return []\n const entries = readdirSync(storesRoot, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => {\n const full = join(storesRoot, entry.name)\n return { full, mtimeMs: statSync(full).mtimeMs }\n })\n .sort((a, b) => b.mtimeMs - a.mtimeMs)\n\n const pruned: string[] = []\n for (const stale of entries.slice(keep)) {\n rmSync(stale.full, { recursive: true, force: true })\n pruned.push(stale.full)\n }\n return pruned\n}\n\nexport function resolveStore(options: ResolveStoreOptions): StoreResolution {\n const { treePath, cacheDir, generations = 4 } = options\n const files = manifestFiles(treePath)\n if (files.length === 0) {\n throw new Error(\n `signoff: no package manifest under ${treePath}. A sign-off run installs from a lockfile; ` +\n 'there is nothing here to install.',\n )\n }\n\n const cacheKey = manifestCacheKey(treePath, files)\n const storesRoot = join(cacheDir, 'stores')\n const storeDir = join(storesRoot, cacheKey)\n // A store directory that exists but holds only the marker is a previous run\n // that died before installing; treat it as a miss so the report does not\n // claim a warm store it does not have.\n const marker = join(storeDir, '.signoff-store.json')\n const hit = existsSync(storeDir) && readdirSync(storeDir).some((entry) => entry !== '.signoff-store.json')\n\n mkdirSync(storeDir, { recursive: true })\n writeFileSync(marker, `${JSON.stringify({ cacheKey, keyedOn: files, usedAt: new Date().toISOString() }, null, 2)}\\n`)\n const now = new Date()\n utimesSync(storeDir, now, now)\n\n return { storeDir, cacheKey, hit, keyedOn: files, pruned: pruneStores(storesRoot, generations) }\n}\n","import { spawnSync } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { copyFileSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport type { SignoffRepoFacts, SignoffSource } from './types'\n\n/**\n * Materialize the pristine checkout every step runs against.\n *\n * **The decision, and the measurement behind it: a `git worktree` of HEAD, not\n * a filtered copy.** Both were timed on two real repos on this host:\n *\n * | repo | `git worktree add --detach` | `rsync -a --exclude node_modules --exclude .git` |\n * |---|---|---|\n * | agent-app (931 tracked files) | **0.04 s / 11 MB** | 0.12 s / 25 MB |\n * | legal-agent (753 tracked files) | **0.06 s / 24 MB** | **3.97 s / 2.7 GB** |\n *\n * The 2.7 GB is the argument. The copy carried `build/`, `.react-router/` and\n * `.wrangler/` — generated output and framework caches — because an exclude\n * list is a hand-maintained enumeration of things to leave behind, and it is\n * never complete. A warm Vite cache is precisely what made the `node:sqlite`\n * bundling failure invisible locally while CI saw it, so a materializer that\n * can leak one has defeated its own purpose. `git` already knows what is source\n * and what is generated, and `.gitignore` is that list, maintained by the repo.\n *\n * The overlay on top is what keeps the gate usable before you commit:\n * `source: 'working-tree'` applies `git diff HEAD` (staged and unstaged) as a\n * patch and copies untracked, non-ignored files in. `source: 'head'` verifies\n * exactly the commit that would merge.\n */\n\nexport interface MaterializeOptions {\n readonly repoDir: string\n readonly dest: string\n readonly source: SignoffSource\n /** Gitignored files the run genuinely needs. Missing ones abort. */\n readonly carryFiles?: readonly string[]\n}\n\nexport interface CleanTree extends SignoffRepoFacts {\n /** Absolute path of the materialized checkout. */\n readonly path: string\n}\n\nfunction git(args: readonly string[], cwd: string): string {\n const result = spawnSync('git', args, { cwd, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 })\n if (result.error) throw new Error(`signoff: git ${args.join(' ')} failed to start: ${result.error.message}`)\n if (result.status !== 0) {\n throw new Error(`signoff: git ${args.join(' ')} exited ${result.status}\\n${result.stderr.trim()}`)\n }\n return result.stdout\n}\n\n/** Split a `-z` separated git list into entries. */\nfunction zsplit(out: string): string[] {\n return out.split('\\0').filter((entry) => entry.length > 0)\n}\n\nexport function repoRootOf(dir: string): string {\n return git(['rev-parse', '--show-toplevel'], dir).trim()\n}\n\nexport function materializeCleanTree(options: MaterializeOptions): CleanTree {\n const { repoDir, dest, source, carryFiles = [] } = options\n const root = repoRootOf(repoDir)\n const head = git(['rev-parse', 'HEAD'], root).trim()\n const branch = git(['rev-parse', '--abbrev-ref', 'HEAD'], root).trim()\n\n // A crashed earlier run leaves a registered worktree whose directory is gone;\n // `add` then refuses on a name collision. Pruning first is what makes the gate\n // re-runnable after a kill.\n git(['worktree', 'prune'], root)\n mkdirSync(dirname(dest), { recursive: true })\n if (existsSync(dest)) rmSync(dest, { recursive: true, force: true })\n git(['worktree', 'add', '--detach', '--quiet', dest, head], root)\n\n let diffSha256: string | null = null\n let untrackedFiles: string[] = []\n\n if (source === 'working-tree') {\n // `git diff HEAD` covers staged and unstaged changes to tracked files in one\n // patch, including deletions and renames. `--binary` keeps a changed image\n // or lockfile-adjacent binary from silently dropping out of the patch.\n const patch = git(['diff', 'HEAD', '--binary', '--no-color', '--no-ext-diff'], root)\n if (patch.length > 0) {\n diffSha256 = createHash('sha256').update(patch).digest('hex')\n const patchFile = join(dirname(dest), `${dest.split('/').pop() ?? 'tree'}.patch`)\n writeFileSync(patchFile, patch)\n // Fail loud: a patch that does not apply means the tree we would verify is\n // not the tree the developer has, and verifying the wrong bytes is worse\n // than not verifying.\n git(['apply', '--binary', '--whitespace=nowarn', patchFile], dest)\n rmSync(patchFile, { force: true })\n }\n\n untrackedFiles = zsplit(git(['ls-files', '--others', '--exclude-standard', '-z'], root))\n for (const rel of untrackedFiles) {\n const target = join(dest, rel)\n mkdirSync(dirname(target), { recursive: true })\n copyFileSync(join(root, rel), target)\n }\n }\n\n const carried: string[] = []\n for (const rel of carryFiles) {\n if (isAbsolute(rel)) throw new Error(`signoff: carryFiles must be repo-relative; got \"${rel}\"`)\n const from = resolve(root, rel)\n if (!existsSync(from)) {\n throw new Error(\n `signoff: carryFiles names \"${rel}\", which does not exist at ${from}. ` +\n 'Remove it from the config or create the file — installing without it would resolve ' +\n 'against a different registry than the one you think you are verifying.',\n )\n }\n const target = join(dest, rel)\n mkdirSync(dirname(target), { recursive: true })\n copyFileSync(from, target)\n carried.push(rel)\n }\n\n return {\n path: dest,\n root,\n head,\n branch,\n source,\n dirty: diffSha256 !== null || untrackedFiles.length > 0,\n diffSha256,\n untrackedFiles,\n carriedFiles: carried,\n }\n}\n\n/** Unregister and delete a materialized tree. */\nexport function removeCleanTree(tree: CleanTree): void {\n git(['worktree', 'remove', '--force', tree.path], tree.root)\n}\n","import { spawnSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { availableParallelism, homedir } from 'node:os'\nimport { basename, join, resolve } from 'node:path'\nimport { loadSignoffConfig } from './config'\nimport { assertNodeVersion, resolveNodeRequirement, type NodeVersionRequirement } from './node-version'\nimport { runCommand, type CommandResult } from './exec'\nimport { runGraph, validateGraph, type TaskOutcome } from './schedule'\nimport { assertShuffleArgsReachTheRunner, newSeedBase, planAttempts } from './seeds'\nimport { resolveStore } from './store'\nimport { materializeCleanTree, removeCleanTree, repoRootOf, type CleanTree } from './workspace'\nimport type {\n SignoffAttempt,\n SignoffEvent,\n SignoffHostFacts,\n SignoffInstallResult,\n SignoffReport,\n SignoffSource,\n SignoffStepResult,\n SignoffStepSpec,\n} from './types'\n\n/**\n * The sign-off run: reproduce a clean CI environment, then go further.\n *\n * Order matters and each stage exists for a failure this fleet actually paid\n * for:\n * 1. a pristine checkout (`workspace.ts`) — no warm `node_modules`, no Vite\n * cache, because that is the entire mechanical difference between \"green\n * locally\" and \"red in CI\";\n * 2. `--frozen-lockfile` into a store keyed on the lockfile (`store.ts`) — the\n * clean install CI does, without paying the download twice;\n * 3. the repo's declared steps (`config.ts`), run as a graph rather than a line\n * (`schedule.ts`), with the suite re-run under randomized file order and\n * recorded seeds (`seeds.ts`) — the part CI does not do at all.\n *\n * Nothing about a failure is inferred. Every step reports its command, exit\n * code, duration, seed and captured output.\n */\n\nexport interface RunSignoffOptions {\n /** Any directory inside the repo. Defaults to the process cwd. */\n readonly repoDir?: string\n readonly configPath?: string\n /** Default `working-tree` — verify what you are about to commit. */\n readonly source?: SignoffSource\n /** Run every step even after one fails. Default false. */\n readonly keepGoing?: boolean\n /** Base seed. Pass a previous run's to reproduce it exactly. */\n readonly seed?: number\n readonly maxParallel?: number\n readonly cacheDir?: string\n /** Override every shuffled step's run count. */\n readonly shuffleRuns?: number\n /** Keep the clean tree for inspection instead of removing it. */\n readonly keepWorkspace?: boolean\n readonly onEvent?: (event: SignoffEvent) => void\n}\n\nconst DEFAULT_CACHE_DIR = join(homedir(), '.cache', 'agent-app-signoff')\n\nfunction hostFacts(treePath: string, requirement: NodeVersionRequirement | null): SignoffHostFacts {\n const pm = spawnSync('pnpm', ['--version'], { cwd: treePath, encoding: 'utf8' })\n return {\n node: process.version,\n nodePinned: requirement?.declared ?? null,\n nodePinSource: requirement?.source ?? null,\n packageManager: pm.status === 0 ? `pnpm ${pm.stdout.trim()}` : 'pnpm (not resolvable)',\n platform: process.platform,\n arch: process.arch,\n cpus: availableParallelism(),\n }\n}\n\n/** Successful steps keep only a tail; a passing 3,000-test run is not evidence. */\nconst SUCCESS_OUTPUT_TAIL = 4_000\n\nfunction toAttempt(result: CommandResult, seed: number | null, ok: boolean): SignoffAttempt {\n return {\n command: result.command,\n seed,\n exitCode: result.exitCode,\n signal: result.signal,\n durationMs: result.durationMs,\n timedOut: result.timedOut,\n output: ok ? result.output.slice(-SUCCESS_OUTPUT_TAIL) : result.output,\n outputTruncated: result.truncated || (ok && result.output.length > SUCCESS_OUTPUT_TAIL),\n }\n}\n\nfunction buildEnv(\n base: Readonly<Record<string, string | undefined>>,\n layers: readonly (Readonly<Record<string, string>> | undefined)[],\n): Record<string, string | undefined> {\n const env: Record<string, string | undefined> = { ...base }\n for (const layer of layers) {\n if (layer) Object.assign(env, layer)\n }\n return env\n}\n\n/** Where the store flag goes: after the subcommand, so `pnpm install X --store-dir Y` stays valid. */\nfunction withStoreDir(command: string, flag: string | null | undefined, storeDir: string): string {\n if (flag === null) return command\n return `${command} ${flag ?? '--store-dir'} ${JSON.stringify(storeDir)}`\n}\n\nexport async function runSignoff(options: RunSignoffOptions = {}): Promise<SignoffReport> {\n const startedAt = new Date()\n const wallStart = Date.now()\n const repoDir = resolve(options.repoDir ?? process.cwd())\n const repoRoot = repoRootOf(repoDir)\n const { config, origin } = await loadSignoffConfig({ repoRoot, configPath: options.configPath })\n\n // Fail before materializing anything: a bad graph is a config error, and\n // paying for an install to learn that is a waste of the speed this exists for.\n validateGraph(config.steps.map((step) => ({ name: step.name, needs: step.needs })))\n assertShuffleArgsReachTheRunner(config.steps)\n const nodeRequirement = resolveNodeRequirement(repoRoot, config.nodeVersion)\n assertNodeVersion(nodeRequirement)\n\n const source = options.source ?? 'working-tree'\n const cacheDir = resolve(options.cacheDir ?? config.cacheDir ?? DEFAULT_CACHE_DIR)\n // The tree lives under the same root as the stores on purpose: pnpm hardlinks\n // from the store into `node_modules`, and a store on another filesystem\n // silently degrades to a full copy.\n const treePath = join(cacheDir, 'trees', `${basename(repoRoot)}-${process.pid}`)\n\n let tree: CleanTree | null = null\n try {\n tree = materializeCleanTree({ repoDir: repoRoot, dest: treePath, source, carryFiles: config.carryFiles })\n options.onEvent?.({ kind: 'tree', path: tree.path, head: tree.head, dirty: tree.dirty })\n\n const store = resolveStore({ treePath: tree.path, cacheDir, generations: config.storeGenerations })\n options.onEvent?.({ kind: 'store', storeDir: store.storeDir, cacheHit: store.hit, cacheKey: store.cacheKey })\n\n const installSpec = config.install ?? {}\n const installCwd = join(tree.path, installSpec.cwd ?? '.')\n const installCommand = withStoreDir(\n installSpec.run ?? 'pnpm install --frozen-lockfile',\n installSpec.storeDirFlag,\n store.storeDir,\n )\n const storeEnvName = installSpec.storeEnv === null ? null : installSpec.storeEnv ?? 'NPM_CONFIG_STORE_DIR'\n const sharedEnv = buildEnv(process.env, [\n // Parity with CI: a runner that behaves differently under `CI` (vitest's\n // reporter, wrangler's prompts) must behave that way here too.\n { CI: 'true' },\n config.env,\n storeEnvName === null ? undefined : { [storeEnvName]: store.storeDir },\n ])\n\n options.onEvent?.({ kind: 'install-start', command: installCommand })\n const installResult = await runCommand({\n command: installCommand,\n cwd: installCwd,\n env: buildEnv(sharedEnv, [installSpec.env]),\n timeoutMs: installSpec.timeoutMs,\n })\n options.onEvent?.({ kind: 'install-end', exitCode: installResult.exitCode, durationMs: installResult.durationMs })\n\n const install: SignoffInstallResult = {\n command: installCommand,\n storeDir: store.storeDir,\n cacheKey: store.cacheKey,\n cacheHit: store.hit,\n keyedOn: store.keyedOn,\n exitCode: installResult.exitCode,\n durationMs: installResult.durationMs,\n output: installResult.exitCode === 0 ? installResult.output.slice(-SUCCESS_OUTPUT_TAIL) : installResult.output,\n outputTruncated: installResult.truncated,\n }\n\n const host = hostFacts(tree.path, nodeRequirement)\n const seedBase = options.seed ?? newSeedBase()\n\n if (installResult.exitCode !== 0) {\n // Every step is unrunnable, and saying so is the honest report. A gate\n // that reports \"0 failures\" because it never ran anything is the failure\n // mode this whole module exists to prevent.\n return finish({\n ok: false,\n startedAt,\n wallStart,\n tree,\n origin,\n host,\n install,\n steps: config.steps.map(\n (step): SignoffStepResult => ({\n name: step.name,\n status: 'skipped',\n attempts: [],\n durationMs: 0,\n startedAtMs: null,\n finishedAtMs: null,\n }),\n ),\n seedBase,\n keepGoing: options.keepGoing ?? false,\n workspaceRetained: options.keepWorkspace ?? false,\n source,\n options,\n })\n }\n\n const treeRoot = tree.path\n const outcomes = await runGraph<SignoffStepSpec, readonly SignoffAttempt[]>({\n nodes: config.steps,\n maxParallel: options.maxParallel ?? config.maxParallel ?? availableParallelism(),\n keepGoing: options.keepGoing ?? false,\n run: async (step, signal) => {\n const attempts: SignoffAttempt[] = []\n for (const plan of planAttempts(step, seedBase, options.shuffleRuns)) {\n options.onEvent?.({ kind: 'step-start', name: step.name, command: plan.command, seed: plan.seed })\n const result = await runCommand({\n command: plan.command,\n cwd: join(treeRoot, step.cwd ?? '.'),\n env: buildEnv(sharedEnv, [step.env]),\n timeoutMs: step.timeoutMs,\n signal,\n })\n const ok = result.exitCode === 0\n attempts.push(toAttempt(result, plan.seed, ok))\n // Stop at the first failing order: the remaining seeds would report\n // the same defect, and the seed that found it is already recorded.\n if (!ok) {\n emitStepEnd(options, step.name, 'failed', attempts)\n return { ok: false, value: attempts }\n }\n }\n emitStepEnd(options, step.name, 'passed', attempts)\n return { ok: true, value: attempts }\n },\n })\n\n const steps = outcomes.map(toStepResult)\n return finish({\n ok: steps.every((step) => step.status === 'passed'),\n startedAt,\n wallStart,\n tree,\n origin,\n host,\n install,\n steps,\n seedBase,\n keepGoing: options.keepGoing ?? false,\n workspaceRetained: options.keepWorkspace ?? false,\n source,\n options,\n })\n } finally {\n if (tree && !options.keepWorkspace && existsSync(tree.path)) removeCleanTree(tree)\n }\n}\n\n/** Emitted from inside the step, so a watching CLI sees a completion when it\n * happens rather than every completion at the end of the run. */\nfunction emitStepEnd(\n options: RunSignoffOptions,\n name: string,\n status: SignoffStepResult['status'],\n attempts: readonly SignoffAttempt[],\n): void {\n options.onEvent?.({\n kind: 'step-end',\n name,\n status,\n durationMs: attempts.reduce((total, attempt) => total + attempt.durationMs, 0),\n })\n}\n\nfunction toStepResult(outcome: TaskOutcome<readonly SignoffAttempt[]>): SignoffStepResult {\n const attempts = outcome.value ?? []\n const durationMs = attempts.reduce((total, attempt) => total + attempt.durationMs, 0)\n return {\n name: outcome.name,\n status: outcome.status,\n attempts,\n durationMs,\n startedAtMs: outcome.startedAtMs,\n finishedAtMs: outcome.finishedAtMs,\n }\n}\n\ninterface FinishInput {\n readonly ok: boolean\n readonly startedAt: Date\n readonly wallStart: number\n readonly tree: CleanTree\n readonly origin: SignoffReport['configOrigin']\n readonly host: SignoffHostFacts\n readonly install: SignoffInstallResult\n readonly steps: readonly SignoffStepResult[]\n readonly seedBase: number\n readonly keepGoing: boolean\n readonly workspaceRetained: boolean\n readonly source: SignoffSource\n readonly options: RunSignoffOptions\n}\n\nfunction finish(input: FinishInput): SignoffReport {\n const serialMs = input.install.durationMs + input.steps.reduce((total, step) => total + step.durationMs, 0)\n const flags = [\n `--source ${input.source}`,\n `--seed ${input.seedBase}`,\n ...(input.keepGoing ? ['--keep-going'] : []),\n ]\n return {\n ok: input.ok,\n startedAt: input.startedAt.toISOString(),\n repo: {\n root: input.tree.root,\n head: input.tree.head,\n branch: input.tree.branch,\n source: input.source,\n dirty: input.tree.dirty,\n diffSha256: input.tree.diffSha256,\n untrackedFiles: input.tree.untrackedFiles,\n carriedFiles: input.tree.carriedFiles,\n },\n configOrigin: input.origin,\n workspace: input.tree.path,\n workspaceRetained: input.workspaceRetained,\n host: input.host,\n install: input.install,\n steps: input.steps,\n seedBase: input.seedBase,\n wallClockMs: Date.now() - input.wallStart,\n serialMs,\n keepGoing: input.keepGoing,\n reproduce: `agent-app-signoff ${input.tree.root} ${flags.join(' ')}`,\n }\n}\n","import type { SignoffReport, SignoffStepResult } from './types'\n\n/**\n * The proof a human reads before merging.\n *\n * A sign-off gate is only worth the CI it replaces if its output answers the\n * question CI's green tick answers and the ones it does not: what bytes were\n * verified, in what environment, under which suite orders, and how to reproduce\n * it. So the report leads with the verdict, states the subject (commit + patch\n * digest) and the environment (clean tree, store cache state), lists every step\n * with its seeds, and ends with the exact command that runs it again.\n *\n * Failure output is printed in full, under the step's name. Naming the step is\n * the difference between \"CI is red\" and a fix.\n */\n\nconst BAR = '─'.repeat(72)\n\nfunction ms(value: number): string {\n return value >= 10_000 ? `${(value / 1000).toFixed(1)}s` : `${value}ms`\n}\n\nfunction statusMark(status: SignoffStepResult['status']): string {\n switch (status) {\n case 'passed': return 'ok '\n case 'failed': return 'FAIL'\n case 'cancelled': return 'kill'\n case 'blocked': return 'blkd'\n case 'skipped': return '-- '\n }\n}\n\nfunction seedList(step: SignoffStepResult): string {\n const seeds = step.attempts.map((attempt) => attempt.seed).filter((seed): seed is number => seed !== null)\n return seeds.length === 0 ? '' : ` seeds ${seeds.join(', ')}`\n}\n\n/** Concurrency actually achieved, measured from the step windows rather than\n * asserted from the config. A claimed speedup nobody measured is a wish. */\nexport function peakConcurrency(steps: readonly SignoffStepResult[]): number {\n const events: { at: number; delta: number }[] = []\n for (const step of steps) {\n if (step.startedAtMs === null || step.finishedAtMs === null) continue\n events.push({ at: step.startedAtMs, delta: 1 }, { at: step.finishedAtMs, delta: -1 })\n }\n events.sort((a, b) => a.at - b.at || a.delta - b.delta)\n let current = 0\n let peak = 0\n for (const event of events) {\n current += event.delta\n peak = Math.max(peak, current)\n }\n return peak\n}\n\nexport function formatSignoffReport(report: SignoffReport): string {\n const lines: string[] = []\n const verdict = report.ok ? 'SIGN-OFF PASSED' : 'SIGN-OFF FAILED'\n lines.push(BAR, `${verdict} — ${report.repo.branch} @ ${report.repo.head.slice(0, 12)}`, BAR, '')\n\n lines.push('subject')\n lines.push(` repo ${report.repo.root}`)\n lines.push(` source ${report.repo.source}${report.repo.dirty ? ' (working tree carries uncommitted work)' : ''}`)\n if (report.repo.diffSha256) lines.push(` patch sha256:${report.repo.diffSha256.slice(0, 16)}`)\n if (report.repo.untrackedFiles.length > 0) {\n lines.push(` untracked ${report.repo.untrackedFiles.length} file(s) copied in`)\n }\n if (report.repo.carriedFiles.length > 0) lines.push(` carried ${report.repo.carriedFiles.join(', ')}`)\n lines.push('')\n\n lines.push('environment')\n lines.push(` clean tree ${report.workspace}${report.workspaceRetained ? ' (retained)' : ' (removed)'}`)\n lines.push(` install ${report.install.command}`)\n lines.push(\n ` store ${report.install.cacheHit ? 'warm' : 'cold'} — ${report.install.cacheKey.slice(0, 16)} ` +\n `(keyed on ${report.install.keyedOn.length} manifest file(s))`,\n )\n lines.push(\n ` host ${report.host.node} · ${report.host.packageManager} · ${report.host.cpus} cpus` +\n (report.host.nodePinned === null\n ? ' · node UNPINNED by this repo'\n : ` · pinned ${report.host.nodePinned} (${report.host.nodePinSource})`),\n )\n lines.push(\n ` config ${report.configOrigin.kind === 'derived'\n ? `derived from scripts: ${report.configOrigin.scripts.join(', ')}`\n : report.configOrigin.path}`,\n )\n lines.push('')\n\n const width = Math.max(...report.steps.map((step) => step.name.length), 'install'.length)\n lines.push('steps')\n lines.push(\n ` ${report.install.exitCode === 0 ? 'ok ' : 'FAIL'} ${'install'.padEnd(width)} ${ms(report.install.durationMs).padStart(8)}`,\n )\n for (const step of report.steps) {\n const window =\n step.startedAtMs === null || step.finishedAtMs === null\n ? ''\n : ` [${ms(step.startedAtMs)} → ${ms(step.finishedAtMs)}]`\n lines.push(\n ` ${statusMark(step.status)} ${step.name.padEnd(width)} ${ms(step.durationMs).padStart(8)}` +\n ` ${step.attempts.length} run(s)${window}${seedList(step)}`,\n )\n }\n lines.push('')\n\n const peak = peakConcurrency(report.steps)\n const saved = report.serialMs - report.wallClockMs\n lines.push('timing')\n lines.push(` wall clock ${ms(report.wallClockMs)}`)\n lines.push(` serial sum ${ms(report.serialMs)} (install + every step, one after another)`)\n lines.push(\n ` parallel peak ${peak} step(s) at once — ` +\n (saved > 0 ? `${ms(saved)} saved, ${(report.serialMs / report.wallClockMs).toFixed(2)}x` : 'no overlap available'),\n )\n lines.push('')\n\n const failures = report.steps.filter((step) => step.status === 'failed' || step.status === 'cancelled')\n if (report.install.exitCode !== 0) {\n lines.push(BAR, 'install FAILED — no step could run', BAR, report.install.output.trimEnd(), '')\n }\n for (const step of failures) {\n const last = step.attempts[step.attempts.length - 1]\n lines.push(BAR)\n lines.push(`${step.status === 'cancelled' ? 'CANCELLED' : 'FAILED'}: ${step.name}`)\n if (last) {\n lines.push(` command ${last.command}`)\n lines.push(` exit ${last.exitCode}${last.signal ? ` (${last.signal})` : ''}${last.timedOut ? ' — TIMED OUT' : ''}`)\n if (last.seed !== null) {\n lines.push(` seed ${last.seed} — replay this order alone with the same seed`)\n }\n lines.push(BAR, last.output.trimEnd(), '')\n }\n }\n\n const blocked = report.steps.filter((step) => step.status === 'blocked' || step.status === 'skipped')\n if (blocked.length > 0) {\n lines.push(`not judged: ${blocked.map((step) => `${step.name} (${step.status})`).join(', ')}`)\n lines.push('')\n }\n\n lines.push(`reproduce: ${report.reproduce}`)\n return lines.join('\\n')\n}\n\n/** One line for a commit message, a PR comment, or a chat handoff. */\nexport function formatSignoffLine(report: SignoffReport): string {\n const passed = report.steps.filter((step) => step.status === 'passed').length\n return (\n `${report.ok ? 'signoff PASS' : 'signoff FAIL'} ${report.repo.head.slice(0, 12)} — ` +\n `${passed}/${report.steps.length} steps, ${ms(report.wallClockMs)} wall ` +\n `(${ms(report.serialMs)} serial), seed ${report.seedBase}, ` +\n `${report.install.cacheHit ? 'warm' : 'cold'} store, clean install`\n )\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","majorOf","major","existsSync","readFileSync","join","join","existsSync","readFileSync","resolve","createHash","existsSync","readFileSync","readdirSync","join","createHash","existsSync","mkdirSync","rmSync","writeFileSync","join","resolve","spawnSync","existsSync","join","resolve","join","spawnSync","resolve","existsSync"]}
|