@homeflare/config 0.10.0 → 0.11.1

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.
Files changed (40) hide show
  1. package/README.md +11 -41
  2. package/bin/hooks.ts +8 -4
  3. package/dist/hooks/activate.d.ts +7 -0
  4. package/dist/hooks/activate.d.ts.map +1 -0
  5. package/dist/hooks/gates.d.ts +7 -14
  6. package/dist/hooks/gates.d.ts.map +1 -1
  7. package/dist/hooks/install.d.ts +7 -1
  8. package/dist/hooks/install.d.ts.map +1 -1
  9. package/dist/hooks/oxfmt-config.d.ts +25 -0
  10. package/dist/hooks/oxfmt-config.d.ts.map +1 -0
  11. package/dist/hooks/push-plan.d.ts +59 -0
  12. package/dist/hooks/push-plan.d.ts.map +1 -0
  13. package/dist/hooks/push-range.d.ts +51 -0
  14. package/dist/hooks/push-range.d.ts.map +1 -0
  15. package/dist/hooks/report.d.ts +39 -2
  16. package/dist/hooks/report.d.ts.map +1 -1
  17. package/dist/hooks/secrets.d.ts +2 -0
  18. package/dist/hooks/secrets.d.ts.map +1 -0
  19. package/dist/hooks.d.ts +30 -6
  20. package/dist/hooks.d.ts.map +1 -1
  21. package/dist/hooks.js +408 -41
  22. package/dist/hooks.js.map +12 -7
  23. package/dist/repo-shape/yaml.d.ts +20 -3
  24. package/dist/repo-shape/yaml.d.ts.map +1 -1
  25. package/dist/repo-shape.js +8 -2
  26. package/dist/repo-shape.js.map +3 -3
  27. package/dist/versions.js +8 -2
  28. package/dist/versions.js.map +3 -3
  29. package/docs/hooks.md +118 -0
  30. package/package.json +1 -1
  31. package/src/hooks/activate.ts +71 -0
  32. package/src/hooks/gates.ts +156 -39
  33. package/src/hooks/install.ts +39 -20
  34. package/src/hooks/oxfmt-config.ts +67 -0
  35. package/src/hooks/push-plan.ts +167 -0
  36. package/src/hooks/push-range.ts +177 -0
  37. package/src/hooks/report.ts +46 -6
  38. package/src/hooks/secrets.ts +42 -0
  39. package/src/hooks.ts +30 -14
  40. package/src/repo-shape/yaml.ts +26 -4
package/dist/hooks.js.map CHANGED
@@ -1,14 +1,19 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/hooks/report.ts", "../src/hooks/staged.ts", "../src/hooks/gates.ts", "../src/hooks/install.ts", "../src/hooks.ts"],
3
+ "sources": ["../src/hooks/report.ts", "../src/hooks/activate.ts", "../src/hooks/gates.ts", "../src/hooks/oxfmt-config.ts", "../src/hooks/push-plan.ts", "../src/hooks/push-range.ts", "../src/hooks/secrets.ts", "../src/hooks/staged.ts", "../src/hooks/install.ts", "../src/hooks.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * How a hook talks to whoever triggered it.\n *\n * ★ EVERY FAILURE PRINTS BOTH COMMANDS — the one that fixes it and the one that skips\n * it. A hook that exits non-zero and says nothing teaches `--no-verify` as a reflex,\n * and that switch turns off every check rather than the one that was wrong.\n *\n * ⚠️ Output goes to STDERR. Git hooks share stdout with porcelain in some flows, and a\n * hook that writes there can corrupt what a caller is parsing.\n */\n\n/** The two hooks this package implements. Named exactly as the git hook files are. */\nexport type Hook = 'pre-commit' | 'pre-push';\n\nconst BYPASS: Record<Hook, string> = {\n 'pre-commit': 'git commit --no-verify',\n 'pre-push': 'git push --no-verify',\n};\n\n/**\n * 🔴 A GIT HOOK EXPORTS `GIT_DIR` AND `GIT_INDEX_FILE`, AND EVERYTHING IT SPAWNS\n * INHERITS THEM. Measured 2026-09-22 at the cost of two junk files and a stray commit\n * on this repository's `main`: `pre-push` runs `bun run check`, `check` runs the test\n * suite, and a test that builds a throwaway git repository and commits in it commits\n * into THIS repository instead — `cwd` is ignored once `GIT_DIR` is set. The estate's\n * own `packages/site/tests/checkout.test.ts` already carried a comment warning about\n * exactly this, which is how much a convention is worth.\n * ⛔ So the hook strips them rather than trusting fourteen test suites to remember. The\n * gate must see the repository through `cwd`, the way CI does.\n */\nfunction withoutGitEnv(): Record<string, string | undefined> {\n return Object.fromEntries(\n Object.entries(process.env).filter(([name]) => !name.startsWith('GIT_')),\n );\n}\n\n/**\n * Run a command, streaming its output. Returns its exit code.\n * `isolated` drops the inherited `GIT_*` variables — see `withoutGitEnv`.\n */\nexport async function run(cmd: readonly string[], isolated = false): Promise<number> {\n const proc = Bun.spawn([...cmd], {\n stdout: 'inherit',\n stderr: 'inherit',\n ...(isolated ? { env: withoutGitEnv() } : {}),\n });\n return await proc.exited;\n}\n\n/** Capture a command's stdout. Used for git plumbing only. */\nexport async function capture(cmd: readonly string[]): Promise<string> {\n const proc = Bun.spawn([...cmd], { stdout: 'pipe', stderr: 'ignore' });\n const text = await new Response(proc.stdout).text();\n await proc.exited;\n return text;\n}\n\n/**\n * Resolve a dev tool to the project's own copy.\n *\n * ⚠️ NOT `bunx` BY DEFAULT. On a cache miss `bunx` downloads from the registry, and a\n * git hook that reaches the network mid-commit is a hang waiting for a flaky link.\n * husky puts `node_modules/.bin` on PATH, but this runs outside husky in tests, so\n * the local binary is named outright when it exists and `bunx` is only the fallback.\n */\nexport function tool(root: string, name: string): readonly string[] {\n const local = `${root}/node_modules/.bin/${name}`;\n return Bun.file(local).size > 0 ? [local] : ['bunx', name];\n}\n\n/**\n * ⚠️ `process.stderr.write`, NOT `console`. Two reasons, and the lint rule is the lesser\n * one: a hook shares stdout with git porcelain in some flows, and a synchronous write\n * is the only kind guaranteed to land before `process.exit` below throws the buffer\n * away. Using `console.error` here would also make every consumer of this package\n * need a `no-console` exemption for code they never call directly.\n */\nfunction line(text: string): void {\n process.stderr.write(`${text}\\n`);\n}\n\nexport function ok(what: string): void {\n line(`✓ ${what}`);\n}\n\nexport function note(what: string): void {\n line(` ${what}`);\n}\n\n/** ⛔ ALWAYS GIVE THE FIX. \"lint failed\" is a dead end; the command that repairs it is not. */\nexport function fail(hook: Hook, what: string, fix: string): never {\n line(`\\n✗ ${hook}: ${what}`);\n line(` fix: ${fix}`);\n line(` bypass: ${BYPASS[hook]} — CI still runs the real gate\\n`);\n process.exit(1);\n}\n",
5
+ "/**\n * How a hook talks to whoever triggered it.\n *\n * ★ EVERY FAILURE PRINTS BOTH COMMANDS — the one that fixes it and the one that skips\n * it. A hook that exits non-zero and says nothing teaches `--no-verify` as a reflex,\n * and that switch turns off every check rather than the one that was wrong.\n *\n * ⚠️ Output goes to STDERR. Git hooks share stdout with porcelain in some flows, and a\n * hook that writes there can corrupt what a caller is parsing.\n */\n\n/** The two hooks this package implements. Named exactly as the git hook files are. */\nexport type Hook = 'pre-commit' | 'pre-push';\n\nconst BYPASS: Record<Hook, string> = {\n 'pre-commit': 'git commit --no-verify',\n 'pre-push': 'git push --no-verify',\n};\n\n/**\n * 🔴 A GIT HOOK EXPORTS `GIT_DIR` AND `GIT_INDEX_FILE`, AND EVERYTHING IT SPAWNS\n * INHERITS THEM. Measured 2026-09-22 at the cost of two junk files and a stray commit\n * on this repository's `main`: `pre-push` runs `bun run check`, `check` runs the test\n * suite, and a test that builds a throwaway git repository and commits in it commits\n * into THIS repository instead — `cwd` is ignored once `GIT_DIR` is set. The estate's\n * own `packages/site/tests/checkout.test.ts` already carried a comment warning about\n * exactly this, which is how much a convention is worth.\n * ⛔ So the hook strips them rather than trusting fourteen test suites to remember. The\n * gate must see the repository through `cwd`, the way CI does.\n */\nexport function withoutGitEnv(): Record<string, string | undefined> {\n return Object.fromEntries(\n Object.entries(process.env).filter(([name]) => !name.startsWith('GIT_')),\n );\n}\n\n/**\n * Run a command, streaming its output. Returns its exit code.\n * `isolated` drops the inherited `GIT_*` variables — see `withoutGitEnv`.\n */\nexport async function run(cmd: readonly string[], isolated = false): Promise<number> {\n const proc = Bun.spawn([...cmd], {\n stdout: 'inherit',\n stderr: 'inherit',\n ...(isolated ? { env: withoutGitEnv() } : {}),\n });\n return await proc.exited;\n}\n\n/** Capture a command's stdout. Used for git plumbing only. */\nexport async function capture(cmd: readonly string[]): Promise<string> {\n return (await probe(cmd)).stdout;\n}\n\n/**\n * Run a command, relaying its stdout live-enough (after it exits) while also handing the\n * caller the text — gates.ts reads oxfmt's own \"on N files\" summary from it. `stderr` stays\n * `inherit`: diagnostics (a parse error, \"no files matched\") must show immediately, and nothing\n * here needs to inspect them.\n */\nexport async function runCaptured(\n cmd: readonly string[],\n): Promise<{ readonly code: number; readonly stdout: string }> {\n const proc = Bun.spawn([...cmd], { stdout: 'pipe', stderr: 'inherit' });\n const stdout = await new Response(proc.stdout).text();\n process.stdout.write(stdout);\n return { code: await proc.exited, stdout };\n}\n\n/** Capture a command's stdout AND its exit code — git plumbing that answers by status. */\nexport async function probe(cmd: readonly string[]): Promise<{ code: number; stdout: string }> {\n const proc = Bun.spawn([...cmd], { stdout: 'pipe', stderr: 'ignore' });\n const stdout = await new Response(proc.stdout).text();\n return { code: await proc.exited, stdout };\n}\n\n/**\n * Run one pre-push lane through `sh -c` at `root`, as `bun run` would run a script line.\n *\n * ⚠️ `node_modules/.bin` FIRST ON PATH, because a lane opened up from a script is no longer\n * run by `bun run`, which is what used to put it there — `vitest` or `oxfmt` in an expanded\n * script would otherwise be \"command not found\" on a machine without a global copy.\n * 🔴 AND WITHOUT THE HOOK'S `GIT_*` — see `withoutGitEnv`.\n */\nexport async function runLane(command: string, root: string): Promise<number> {\n const env = withoutGitEnv();\n env['PATH'] = `${root}/node_modules/.bin:${env['PATH'] ?? ''}`;\n const proc = Bun.spawn(['sh', '-c', command], {\n cwd: root,\n env,\n stdout: 'inherit',\n stderr: 'inherit',\n });\n return await proc.exited;\n}\n\n/**\n * Resolve a dev tool to the project's own copy.\n *\n * ⚠️ NOT `bunx` BY DEFAULT. On a cache miss `bunx` downloads from the registry, and a\n * git hook that reaches the network mid-commit is a hang waiting for a flaky link.\n * Git runs the hook with the caller's PATH, which has no `node_modules/.bin` (husky\n * used to add it; the hooks no longer run through husky), so the local binary is named\n * outright when it exists and `bunx` is only the fallback.\n */\nexport function tool(root: string, name: string): readonly string[] {\n const local = `${root}/node_modules/.bin/${name}`;\n return Bun.file(local).size > 0 ? [local] : ['bunx', name];\n}\n\n/**\n * ⚠️ `process.stderr.write`, NOT `console`. Two reasons, and the lint rule is the lesser\n * one: a hook shares stdout with git porcelain in some flows, and a synchronous write\n * is the only kind guaranteed to land before `process.exit` below throws the buffer\n * away. Using `console.error` here would also make every consumer of this package\n * need a `no-console` exemption for code they never call directly.\n */\nfunction line(text: string): void {\n process.stderr.write(`${text}\\n`);\n}\n\nexport function ok(what: string): void {\n line(`✓ ${what}`);\n}\n\nexport function note(what: string): void {\n line(` ${what}`);\n}\n\n/** ⛔ ALWAYS GIVE THE FIX. \"lint failed\" is a dead end; the command that repairs it is not. */\nexport function fail(hook: Hook, what: string, fix: string): never {\n line(`\\n✗ ${hook}: ${what}`);\n line(` fix: ${fix}`);\n line(` bypass: ${BYPASS[hook]} — CI still runs the real gate\\n`);\n process.exit(1);\n}\n",
6
+ "/**\n * Turn the hooks on for a clone and EVERY worktree of it, from `prepare`.\n *\n * 🔴 THE GAP THIS CLOSES, measured 2026-09-23. husky sets `core.hooksPath=.husky/_`, and\n * `.husky/_` is generated and gitignored: it exists only in the worktree where `bun\n * install` ran husky. `core.hooksPath` lives in the clone's SHARED config, so every other\n * worktree — every `git worktree add` an agent makes — points at a directory it does not\n * have, and git runs no hooks there, silently. In homeflare-kit that was every worktree\n * until `bun install` had run husky inside it.\n * ★ THE FIX IS A TRACKED PATH. `core.hooksPath=.husky` is relative, and git resolves a\n * relative hooks path against the worktree running the hook — so each worktree runs its\n * OWN checked-out `.husky/`, present from the moment the worktree exists. Measured on git\n * 2.55: a fresh `git worktree add` ran the tracked hook with no install at all. (The\n * wrapper then needs that worktree's `node_modules` to do its work — install.ts says so\n * out loud rather than failing.)\n * ⛔ IT NEVER FAILS. `prepare` runs inside `bun install`; a hook problem must not break an\n * install. Every outcome, including \"did nothing\", is a message and exit 0.\n */\nimport { probe } from './report.ts';\n\nexport type Activation = {\n /** The hooks path is set (now or already). */\n readonly active: boolean;\n readonly message: string;\n};\n\n/**\n * ⚠️ CI IS SKIPPED ON PURPOSE. Hooks are a local convenience and CI runs the real gate; a\n * runner with hooks on would run them on the commits it makes itself (a release's\n * \"Version Packages\" commit) on a machine that may have no gitleaks.\n */\nfunction inCi(env: Readonly<Record<string, string | undefined>>): boolean {\n const ci = env['CI'];\n return ci !== undefined && ci !== '' && ci !== '0' && ci.toLowerCase() !== 'false';\n}\n\nexport async function activateHooks(\n root: string,\n env: Readonly<Record<string, string | undefined>> = process.env,\n): Promise<Activation> {\n if (inCi(env)) return { active: false, message: 'CI is set — hooks stay off; CI runs the gate' };\n\n // ★ `--show-prefix` answers two questions at once: whether this is a work tree at all\n // (it fails outside one — a tarball install), and where `root` sits inside it.\n const where = await probe(['git', '-C', root, 'rev-parse', '--show-prefix']);\n if (where.code !== 0) return { active: false, message: 'not a git work tree — nothing to do' };\n\n const tracked = await Promise.all(\n ['pre-commit', 'pre-push'].map((name) => Bun.file(`${root}/.husky/${name}`).exists()),\n );\n if (!tracked.some(Boolean)) {\n return { active: false, message: 'no .husky/pre-commit or .husky/pre-push to point git at' };\n }\n\n // ⚠️ RELATIVE TO THE TOP OF THE WORK TREE, which is where git resolves it — a package\n // below the root (`<prefix>.husky`) still points at its own directory.\n const want = `${where.stdout.trim()}.husky`;\n const current = (\n await probe(['git', '-C', root, 'config', '--get', 'core.hooksPath'])\n ).stdout.trim();\n if (current === want) return { active: true, message: `core.hooksPath is already ${want}` };\n\n // ★ NO `--worktree`: the clone's shared config, so ONE install covers every worktree.\n const set = await probe(['git', '-C', root, 'config', 'core.hooksPath', want]);\n if (set.code !== 0) return { active: false, message: 'git config core.hooksPath failed' };\n const was = current === '' ? 'unset' : current;\n return {\n active: true,\n message: `core.hooksPath ${was} → ${want}, for every worktree of this clone`,\n };\n}\n",
7
+ "/**\n * The two gates every HomeFlare repo gets, from one place.\n *\n * ★ THE SPLIT IS BY COST. `pre-commit` touches only the staged files — a secret scan, then\n * format and lint — and is measured in hundreds of milliseconds, so it can run on every\n * commit without anyone resenting it. `pre-push` runs the repository's own `check`\n * narrowed to what the push can affect (push-plan.ts): seconds, once per push.\n * ⚠️ NEITHER IS A GATE. Both are skippable with `--no-verify`, and a worktree has them only\n * once `bun install` has run there. The required checks on `main` stay the gate — CI\n * runs the whole `check`, every test, the build and the smoke test on every pull request.\n */\nimport { existsSync } from 'node:fs';\nimport { resolveOxfmtConfig } from './oxfmt-config.ts';\nimport { type Lane, planLanes } from './push-plan.ts';\nimport { changesEverything, parsePushRefs, pushScope } from './push-range.ts';\nimport { fail, note, ok, run, runCaptured, runLane, tool } from './report.ts';\nimport { scanStagedSecrets } from './secrets.ts';\nimport { fingerprints, staged } from './staged.ts';\n\n/**\n * oxfmt's own summary line (\"Finished in 2ms on 0 files using 10 threads.\") is how the hook\n * learns whether `--no-error-on-unmatched-pattern` turned a real run into a no-op — oxfmt has\n * no flag that answers this more directly. `null` (unparsable — a future oxfmt wording change)\n * is treated as \"something ran\": that is the safe direction, since it only ever suppresses the\n * new \"no formattable staged files\" message, never a real failure.\n */\nfunction matchedFileCount(stdout: string): number | null {\n const match = /\\bon (\\d+) files? using/.exec(stdout);\n return match?.[1] === undefined ? null : Number(match[1]);\n}\n\n/**\n * Format and lint what is staged, restaging only what the formatter rewrote.\n *\n * ⚠️ THIS HOOK REWRITES FILES DURING A COMMIT, and it says so on the line where it\n * happens. Without the restage, oxfmt would fix the worktree while the commit kept\n * the unformatted bytes — CI then fails on a file that reads as correct locally.\n */\n/**\n * Has `bun install` run in this worktree?\n *\n * ⚠️ WITHOUT IT, SKIP — LOUDLY — RATHER THAN IMPROVISE. A fresh worktree now runs its hooks\n * (activate.ts), and in the repo that HOSTS this package they are reached by workspace\n * path, not through node_modules. There `tool()` would fall back to `bunx`, fetching an\n * unpinned oxfmt mid-commit, and a pre-push lane would die on \"command not found\". The\n * wrapper every other repo commits makes the same call one step earlier (install.ts).\n */\nfunction installed(root: string, hook: 'pre-commit' | 'pre-push'): boolean {\n if (existsSync(`${root}/node_modules`)) return true;\n note(`${hook}: no node_modules in this worktree — run 'bun install'; skipping the rest`);\n return false;\n}\n\nexport async function preCommit(root: string): Promise<void> {\n // ⛔ SECRETS FIRST, before anything can rewrite or pass — see secrets.ts. It needs only the\n // gitleaks binary, so it runs even in a worktree nobody has installed yet.\n await scanStagedSecrets();\n if (!installed(root, 'pre-commit')) return;\n\n const { formattable, code, partial } = await staged();\n\n // ⚠️ BEFORE RESOLVING A CONFIG: if oxfmt would never run (nothing formattable staged, not\n // even a partially-staged one to `--check`), a config problem — even an unresolved\n // ambiguous one — must not block the commit. That is the exact shape of BUG 1: don't\n // fail on \"there is nothing to do here\".\n if (formattable.length === 0 && partial.length === 0) {\n ok('pre-commit: nothing staged to format');\n return;\n }\n\n const config = await resolveOxfmtConfig(root);\n if (config.kind === 'ambiguous') {\n fail(\n 'pre-commit',\n `${String(config.files.length)} oxfmt configs found (${config.files.join(', ')}) and oxfmt auto-discovers none of them`,\n 'keep exactly one .oxfmtrc.* file at the repo root (.oxfmtrc.json is auto-discovered)',\n );\n }\n // ⛔ BUG, MEASURED 2026-09-23: bare `oxfmt` only finds `.oxfmtrc.json`/`.oxfmtrc.jsonc` on\n // its own — see oxfmt-config.ts. `--config` is added only when the repo's config needs it.\n const oxfmt = [\n ...tool(root, 'oxfmt'),\n ...(config.kind === 'explicit' ? ['--config', config.path] : []),\n // ⛔ BUG, MEASURED 2026-09-23: a staged file wholly excluded by oxfmt's OWN\n // `ignorePatterns` (house `packages/distilled-*/src/**`, say) made oxfmt exit non-zero\n // with \"Expected at least one target file\" — `staged()` classifies by extension only,\n // blind to the repo's ignore rules, so `formattable`/`code` can be 100% ignored files.\n // This flag is oxfmt's own documented answer (`--help`, oxfmt 0.68.0): still fail on a\n // real formatting problem, but not on \"everything here was excluded\".\n '--no-error-on-unmatched-pattern',\n ];\n const oxlint = [...tool(root, 'oxlint'), '--no-error-on-unmatched-pattern'];\n\n // ⛔ Checked, never rewritten — see staged.ts for why `git add` here would be theft.\n if (partial.length > 0) {\n note(`${partial.length} staged file(s) also have unstaged edits; checking without rewriting`);\n if ((await run([...oxfmt, '--check', ...partial])) !== 0) {\n fail(\n 'pre-commit',\n 'those partially staged files are unformatted, and rewriting them would stage your unstaged work',\n 'bun run format, then stage the files you meant to commit',\n );\n }\n }\n\n if (formattable.length === 0) {\n ok('pre-commit: nothing staged to format');\n return;\n }\n\n const before = await fingerprints(formattable);\n const { code: fmtExit, stdout: fmtOut } = await runCaptured([...oxfmt, ...formattable]);\n if (fmtExit !== 0) {\n fail('pre-commit', 'oxfmt could not format the staged files', 'bun run format');\n }\n const matched = matchedFileCount(fmtOut);\n\n if (matched !== 0) {\n const after = await fingerprints(formattable);\n const rewritten = formattable.filter((file) => before.get(file) !== after.get(file));\n\n if (rewritten.length > 0) {\n note(`oxfmt rewrote and restaged ${rewritten.length} file(s): ${rewritten.join(', ')}`);\n if ((await run(['git', 'add', '--', ...rewritten])) !== 0) {\n fail('pre-commit', 'could not restage the formatted files', 'git add the listed files');\n }\n }\n }\n\n // ⚠️ `--deny-warnings` matches what CI runs. A hook that is laxer than CI is worse\n // than no hook: it certifies a change CI will reject. Same ignore-rules shape as oxfmt\n // above: `code` can be entirely excluded by oxlint's own `ignorePatterns`.\n if (code.length > 0 && (await run([...oxlint, '--deny-warnings', ...code])) !== 0) {\n fail(\n 'pre-commit',\n `oxlint found problems in ${code.length} staged file(s)`,\n 'bun run lint:fix, then fix by hand what remains',\n );\n }\n\n if (matched === 0) {\n note(\n `${String(formattable.length)} staged file(s) matched a formattable extension, but all are excluded by ignore rules`,\n );\n ok('pre-commit: no formattable staged files');\n } else {\n ok(`pre-commit: ${formattable.length} staged file(s) formatted and linted`);\n }\n}\n\n/** The one-line reason a lane is in the run, printed before it starts. */\nfunction describe(lane: Lane): string {\n if (lane.kind === 'skip') return `skip ${lane.label} — ${lane.why}`;\n if (lane.kind === 'test' && lane.scoped)\n return `run ${lane.command} (only the tests the push can reach)`;\n if (lane.kind === 'test') return `run ${lane.command} (IN FULL)`;\n return `run ${lane.command}`;\n}\n\n/**\n * Run the repository's own `check`, narrowed to what the push can affect.\n *\n * ★ `args` ARE GIT'S: the remote name and URL. `stdin` is git's ref list — see\n * push-range.ts for how the base is chosen and why it never narrows to nothing.\n * ⛔ IT DOES NOT CERTIFY WHAT CI WILL SAY. It certifies that `check`'s own lint and type\n * lanes pass and that every test the pushed files can reach passes. The build, the smoke\n * test and the unreachable tests are CI's, and the success line says so.\n */\nexport async function prePush(root: string, args: readonly string[], stdin: string): Promise<void> {\n const manifest = Bun.file(`${root}/package.json`);\n const pkg = (await manifest.exists())\n ? ((await manifest.json()) as { scripts?: Record<string, string> })\n : {};\n const scripts = pkg.scripts ?? {};\n if (scripts['check'] === undefined) {\n note('pre-push: no `check` script declared in package.json; nothing to run');\n return;\n }\n if (!installed(root, 'pre-push')) return;\n\n const scope = await pushScope(root, args[0] ?? 'origin', parsePushRefs(stdin));\n if (scope.kind === 'empty') {\n ok(`pre-push: ${scope.why} — nothing to check`);\n return;\n }\n if (scope.kind === 'elsewhere') {\n // ⛔ NOT A PASS, AND IT DOES NOT SAY ONE. The working tree is another commit; running the\n // lanes would certify content nobody checked. Failing would teach `--no-verify` for an\n // ordinary push, so it says what it did not do, and CI checks the ref.\n note(`pre-push: ${scope.why} — the working tree is not what is being pushed`);\n note(\n ' NOT CHECKED here; CI checks it. To check it locally, check it out and push from there.',\n );\n return;\n }\n let base: string | undefined;\n if (scope.kind === 'unscoped') {\n note(`pre-push: ${scope.why} — every lane runs, tests in full`);\n } else {\n const global = scope.changed.filter(changesEverything);\n note(`pre-push: ${String(scope.changed.length)} file(s) changed ${scope.why}`);\n if (global.length > 0)\n note(` ${global.join(', ')} changes what every test runs on — tests in full`);\n else base = scope.base;\n }\n\n const lanes = planLanes(scripts, base);\n const started = Bun.nanoseconds();\n let ran = 0;\n for (const lane of lanes) {\n note(describe(lane));\n if (lane.kind === 'skip') continue;\n // 🔴 `runLane` strips the GIT_* this hook inherited — see report.ts.\n if ((await runLane(lane.command, root)) !== 0) {\n fail('pre-push', `\\`${lane.label}\\` failed`, `${lane.command} — until it is green`);\n }\n ran += 1;\n }\n const seconds = ((Bun.nanoseconds() - started) / 1e9).toFixed(1);\n ok(`pre-push: ${String(ran)} lane(s) of \\`check\\` passed in ${seconds}s — CI runs the full gate`);\n}\n",
8
+ "/**\n * Which `.oxfmtrc.*` file governs this repo — so the hook can name it explicitly.\n *\n * ⛔ BUG, MEASURED 2026-09-23 (homeflare-desktop docs agent): the shared pre-commit ran\n * bare `oxfmt`, and bare `oxfmt` auto-discovers only `.oxfmtrc.json` and `.oxfmtrc.jsonc`.\n * Checked against the pinned oxfmt 0.68.0 directly: a repo with only `.oxfmtrc.mjs` and\n * no `-c` prints \"No config found, using defaults\" and formats with oxfmt's built-in\n * style, silently — even though `oxfmt --help` lists `.ts/.mts/.cts/.js/.mjs/.cjs` as\n * valid `--config` targets. Desktop's fix was renaming to `.oxfmtrc.json`; this fixes the\n * hook so a repo does not have to.\n * ★ WHY A FILE SCAN, NOT A PACKAGE.JSON READ. The repo's own `format`/`check` script names\n * the config it wants (or relies on auto-discovery finding `.oxfmtrc.json`), but its exact\n * invocation shape varies per repo. Finding the config file on disk and handing it to\n * oxfmt directly gets the same result without parsing an arbitrary shell command.\n * ⛔ EVERY `.oxfmtrc.*` LIVES AT THE REPO ROOT TODAY (checked: every repo in the estate has\n * exactly one, at root — no nested per-package overrides). Explicit `-c` disables oxfmt's\n * own nested-config search (measured: a nested `.oxfmtrc.json` was ignored once `-c` named\n * the root one), so this only scans `root` itself — it would misfire on a repo that added a\n * package-level override, which none currently do.\n */\nimport { readdir } from 'node:fs/promises';\n\n/** Auto-discovered by bare `oxfmt` — nothing for the hook to pass. */\nconst AUTO_DISCOVERED = ['.oxfmtrc.json', '.oxfmtrc.jsonc'];\n\n/**\n * Accepted by `oxfmt -c/--config` (per `oxfmt --help`, oxfmt 0.68.0) but never found on its\n * own — checked empirically for each extension. Listed in the order `--help` documents them.\n */\nconst NEEDS_EXPLICIT_CONFIG = [\n '.oxfmtrc.ts',\n '.oxfmtrc.mts',\n '.oxfmtrc.cts',\n '.oxfmtrc.js',\n '.oxfmtrc.mjs',\n '.oxfmtrc.cjs',\n];\n\nexport type OxfmtConfigResolution =\n /** `.oxfmtrc.json` or `.oxfmtrc.jsonc` exists — bare oxfmt already finds it. */\n | { readonly kind: 'auto' }\n /** No `.oxfmtrc.*` at all — let oxfmt use its built-in defaults, as today. */\n | { readonly kind: 'none' }\n /** Exactly one config oxfmt would not find unaided — pass it with `-c`. */\n | { readonly kind: 'explicit'; readonly path: string }\n /**\n * Two or more configs, none of them auto-discovered — bare oxfmt (and a repo's own\n * bare `bun run format`) would silently fall back to defaults too, honouring neither.\n * Guessing which one the repo meant would just move the silent-defaults bug here.\n */\n | { readonly kind: 'ambiguous'; readonly files: readonly string[] };\n\n/** Every `.oxfmtrc.*` file actually present at `root`, in a fixed, deterministic order. */\nasync function present(root: string, names: readonly string[]): Promise<readonly string[]> {\n const entries = new Set(await readdir(root).catch(() => []));\n return names.filter((name) => entries.has(name));\n}\n\nexport async function resolveOxfmtConfig(root: string): Promise<OxfmtConfigResolution> {\n if ((await present(root, AUTO_DISCOVERED)).length > 0) return { kind: 'auto' };\n\n const explicit = await present(root, NEEDS_EXPLICIT_CONFIG);\n const [only, ...rest] = explicit;\n if (only === undefined) return { kind: 'none' };\n if (rest.length === 0) return { kind: 'explicit', path: `${root}/${only}` };\n return { kind: 'ambiguous', files: explicit };\n}\n",
9
+ "/**\n * What `pre-push` runs: the repository's own `check`, with the expensive lanes narrowed to\n * the push or left to CI.\n *\n * ★ IT READS `check` RATHER THAN NAMING TOOLS. `check` is the command CI runs, so the lanes\n * here are that command's lanes — a repository that adds a step to `check` gets it in the\n * hook with no change to this package. Hard-coding `tsc` and `bun test` would drift from\n * whichever repository added a step first, and certify a push CI rejects.\n * ★ THREE THINGS CHANGE, AND ONLY THREE:\n * 1. every `bun test …` becomes `bun test … --changed=<base>` — Bun's own import-graph\n * answer to \"which test files can these changed files reach\" (bun 1.4, measured\n * 2026-09-23: 11 changed files ran 2 of 246 test files in homeflare-kit);\n * 2. `build` and `smoke` scripts are skipped — minutes in a big workspace, and CI runs\n * both on every pull request;\n * 3. everything else (lint, types, a `--check` script) runs exactly as `check` spells it.\n * They are seconds, whole-program by nature, and deterministic.\n * ⛔ PURE. No git, no filesystem, no process: the whole contract is a function of the\n * scripts table and the base, so the estate's real `check` shapes are pinned by a table\n * test (tests/hooks-push-plan.test.ts) instead of by a live push.\n */\n\n/** One step of the pre-push run. */\nexport type Lane =\n /** Run `command` through `sh -c` at the repository root. */\n | { readonly kind: 'run'; readonly label: string; readonly command: string }\n /** A test runner; `scoped` says whether it was narrowed to the push. */\n | {\n readonly kind: 'test';\n readonly label: string;\n readonly command: string;\n readonly scoped: boolean;\n }\n /** Not run here, and why. */\n | { readonly kind: 'skip'; readonly label: string; readonly why: string };\n\n/**\n * ⚠️ BY NAME, AND ONLY THESE. A build in homeflare-kit is every package; a smoke test packs\n * and installs tarballs. Both are CI jobs on every pull request, and neither says anything\n * a type check and the reachable tests did not already say about a typical push.\n */\nconst LEFT_TO_CI = /^(build|smoke)(:|$)/;\n\n/** Recursion guard: a script that names itself, directly or through another, stops here. */\nconst MAX_DEPTH = 8;\n\n/**\n * Split a script on top-level `&&`, or return undefined when it is anything else.\n *\n * ⛔ UNDEFINED MEANS \"RUN IT WHOLE\". `||`, `;`, a pipe, a redirect, a background `&` or a\n * substitution each change what the pieces mean together, and a hook that re-plumbed them\n * would run something the repository never wrote. Quoted text is left alone, so\n * `--path-ignore-patterns=\"homeflare-*\\/**\"` survives intact.\n */\nexport function andChain(script: string): readonly string[] | undefined {\n const parts: string[] = [];\n let quote: string | undefined;\n let current = '';\n for (let i = 0; i < script.length; i++) {\n const char = script[i] ?? '';\n if (quote !== undefined) {\n if (char === quote) quote = undefined;\n current += char;\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '&' && script[i + 1] === '&') {\n parts.push(current.trim());\n current = '';\n i += 1;\n continue;\n }\n if ('|;&<>`'.includes(char) || (char === '$' && script[i + 1] === '(')) return undefined;\n current += char;\n }\n if (quote !== undefined) return undefined;\n parts.push(current.trim());\n return parts.some((part) => part === '') ? undefined : parts;\n}\n\n/** The script a segment names: `bun run x`, `npm run x`, or `npm test`. Else undefined. */\nfunction scriptRef(segment: string, scripts: Readonly<Record<string, string>>): string | undefined {\n const words = segment.split(/\\s+/);\n if (words.length === 2 && words[0] === 'npm' && words[1] === 'test') return 'test';\n const [runner, verb, name] = words;\n if (words.length !== 3 || verb !== 'run' || name === undefined) return undefined;\n if (runner !== 'bun' && runner !== 'npm') return undefined;\n return Object.hasOwn(scripts, name) ? name : undefined;\n}\n\n/** `bun test …` — the runner, not a script called `test`. */\nconst isBunTest = (segment: string): boolean => /^bun\\s+test(\\s|$)/.test(segment);\n\nfunction testLane(segment: string, base: string | undefined): Lane {\n return base === undefined\n ? { kind: 'test', label: segment, command: segment, scoped: false }\n : { kind: 'test', label: segment, command: `${segment} --changed=${base}`, scoped: true };\n}\n\nfunction expand(\n script: string,\n scripts: Readonly<Record<string, string>>,\n base: string | undefined,\n depth: number,\n): readonly Lane[] | undefined {\n const segments = andChain(script);\n if (segments === undefined || depth > MAX_DEPTH) return undefined;\n const lanes: Lane[] = [];\n for (const segment of segments) {\n if (isBunTest(segment)) {\n lanes.push(testLane(segment, base));\n continue;\n }\n const name = scriptRef(segment, scripts);\n if (name === undefined) {\n lanes.push({ kind: 'run', label: segment, command: segment });\n continue;\n }\n if (LEFT_TO_CI.test(name)) {\n lanes.push({ kind: 'skip', label: segment, why: 'CI runs it on every pull request' });\n continue;\n }\n const inner = expand(scripts[name] ?? '', scripts, base, depth + 1);\n if (inner !== undefined && inner.some((lane) => lane.kind !== 'run')) {\n // Something inside needs narrowing or skipping: open the script up.\n lanes.push(...inner);\n } else if (/^test(:|$)/.test(name)) {\n // ⚠️ A TEST SCRIPT WITH NO `bun test` IN IT RUNS IN FULL, AND SAYS SO — vitest with\n // coverage thresholds (a narrowed run would fail them), `node --test`, anything piped.\n // Narrowing it would mean rewriting a command this package does not understand.\n lanes.push({ kind: 'test', label: segment, command: segment, scoped: false });\n } else {\n // ★ A SCRIPT WITH NOTHING TO NARROW RUNS UNDER ITS OWN NAME. `bun run lint` keeps\n // Bun's PATH handling and reads the way the repository wrote it.\n lanes.push({ kind: 'run', label: segment, command: segment });\n }\n }\n return lanes;\n}\n\n/**\n * The lanes `pre-push` runs for this `scripts` table.\n *\n * `base` is the commit the push is measured from; `undefined` runs every test lane in full\n * (an unknown base, or a push that changes what every test runs on).\n * ⚠️ AN EMPTY LIST MEANS NO `check` SCRIPT — the caller reports that; it is not a pass.\n */\nexport function planLanes(\n scripts: Readonly<Record<string, string>>,\n base: string | undefined,\n): readonly Lane[] {\n const check = scripts['check'];\n if (check === undefined) return [];\n // ⚠️ A `check` THAT IS NOT A PLAIN `&&` CHAIN IS RUN WHOLE, tests and all, and reported\n // as unscoped. No estate repository has one (surveyed 2026-09-23); the fallback exists so\n // an unusual one is checked rather than skipped.\n const whole: Lane = {\n kind: 'test',\n label: 'bun run check',\n command: 'bun run check',\n scoped: false,\n };\n return expand(check, scripts, base, 0) ?? [whole];\n}\n",
10
+ "/**\n * What a push changes: the base it is measured from, and the files between that base and\n * the commit being pushed.\n *\n * ★ THE BASE COMES FROM GIT, NOT FROM A GUESS. A pre-push hook reads\n * `<local ref> <local sha> <remote ref> <remote sha>` on stdin. The remote sha is exactly\n * \"what the remote has now\", so a second push to a branch checks only what is new.\n * 🔴 A BRANCH-NAME BASE IS VACUOUS ON THE BASE BRANCH. Measured in the house monorepo\n * 2026-09-08: `turbo --affected` compares against `main`, so on `main` itself it selected\n * zero packages and a real two-commit push ran zero tests. Taking the base from stdin\n * cannot do that — the remote sha is never the commit being pushed.\n * ⛔ AN UNKNOWN BASE WIDENS, IT NEVER NARROWS TO NOTHING. No merge base (a shallow clone, a\n * rewritten history), no remote-tracking branch: every lane runs, tests in full. A gate\n * whose base is unusable must do MORE work, never quietly run nothing.\n */\nimport { probe } from './report.ts';\n\n/** One line of git's pre-push stdin. */\nexport type PushRef = {\n readonly localRef: string;\n readonly localSha: string;\n readonly remoteRef: string;\n readonly remoteSha: string;\n};\n\nexport type PushScope =\n /** The push changes no file (a deletion, or a branch at its base): nothing to check. */\n | { readonly kind: 'empty'; readonly why: string }\n /** Measured from `base`: `changed` is every path that differs between it and the tip. */\n | {\n readonly kind: 'scoped';\n readonly base: string;\n readonly changed: readonly string[];\n readonly why: string;\n }\n /** No usable base: run every lane, tests in full. */\n | { readonly kind: 'unscoped'; readonly why: string }\n /** The pushed commit is not what is checked out, so the working tree cannot vouch for it. */\n | { readonly kind: 'elsewhere'; readonly why: string };\n\n/** git's \"no such ref\" sentinel — 40 zeros (64 under SHA-256). */\nconst ZERO = /^0+$/;\n\nexport function parsePushRefs(stdin: string): readonly PushRef[] {\n const refs: PushRef[] = [];\n for (const line of stdin.split('\\n')) {\n const [localRef, localSha, remoteRef, remoteSha] = line.trim().split(/\\s+/);\n if (localRef && localSha && remoteRef && remoteSha) {\n refs.push({ localRef, localSha, remoteRef, remoteSha });\n }\n }\n return refs;\n}\n\nconst short = (sha: string): string => sha.slice(0, 9);\n\nasync function ok(root: string, args: readonly string[]): Promise<boolean> {\n return (await probe(['git', '-C', root, ...args])).code === 0;\n}\n\nasync function out(root: string, args: readonly string[]): Promise<string | undefined> {\n const result = await probe(['git', '-C', root, ...args]);\n return result.code === 0 ? result.stdout.trim() : undefined;\n}\n\n/**\n * The remote's default branch as a local ref: `<remote>/HEAD` when the clone recorded it,\n * else `<remote>/main`, else `<remote>/master`.\n * ⚠️ `refs/remotes/<remote>/HEAD` IS OFTEN ABSENT — `git clone` sets it, `git init` plus\n * `git remote add` does not — so the fallbacks are the common path, not the rare one.\n */\nasync function defaultBranch(root: string, remote: string): Promise<string | undefined> {\n const head = await out(root, ['symbolic-ref', '--quiet', `refs/remotes/${remote}/HEAD`]);\n if (head !== undefined && head !== '') return head;\n for (const name of ['main', 'master']) {\n const ref = `refs/remotes/${remote}/${name}`;\n if (await ok(root, ['rev-parse', '--verify', '--quiet', ref])) return ref;\n }\n return undefined;\n}\n\n/**\n * Where the pushed commit is measured from.\n *\n * ★ A FAST-FORWARD IS MEASURED FROM THE REMOTE SHA — only what this push adds.\n * ⚠️ ANYTHING ELSE IS MEASURED FROM THE MERGE BASE WITH THE DEFAULT BRANCH: a new branch\n * (zero remote sha), a remote sha this clone has not fetched, or a force-push after a\n * rebase — where diffing against the old tip would drag in everything `main` gained since.\n */\nasync function baseFor(\n root: string,\n remote: string,\n ref: PushRef,\n): Promise<{ base: string; why: string } | { why: string }> {\n const { localSha, remoteSha } = ref;\n if (\n !ZERO.test(remoteSha) &&\n (await ok(root, ['cat-file', '-e', `${remoteSha}^{commit}`])) &&\n (await ok(root, ['merge-base', '--is-ancestor', remoteSha, localSha]))\n ) {\n return { base: remoteSha, why: `since ${short(remoteSha)}, what ${remote} has now` };\n }\n const branch = await defaultBranch(root, remote);\n if (branch === undefined) return { why: `no ${remote}/main or ${remote}/master to measure from` };\n const mergeBase = await out(root, ['merge-base', localSha, branch]);\n if (mergeBase === undefined || mergeBase === '') {\n return { why: `no merge base with ${branch} (a shallow clone, or unrelated history)` };\n }\n const name = branch.replace(/^refs\\/remotes\\//, '');\n return { base: mergeBase, why: `since ${short(mergeBase)}, where this branch left ${name}` };\n}\n\n/**\n * Resolve the push git described on stdin. With no stdin (a manual run) the push is\n * `HEAD` to a new branch — measured from the merge base with the default branch.\n * 🔴 THE LANES RUN ON THE WORKING TREE, SO ONLY A PUSH OF `HEAD` CAN BE CHECKED HERE. Found\n * in review 2026-09-23 and reproduced: `git push origin broken` from a clean `main`\n * measured the right files, then ran `bun test --changed` against `main`'s tree, found\n * nothing, and printed \"passed\". A ref that is not checked out now comes back\n * `elsewhere`, which the caller reports as NOT CHECKED — never as a pass. (The old\n * whole-`check` hook had the same blind spot; it just ran unrelated tests while in it.)\n * ⚠️ WITH SEVERAL REFS, THE ONE AT `HEAD` IS MEASURED and the rest are named as unchecked.\n */\nexport async function pushScope(\n root: string,\n remote: string,\n refs: readonly PushRef[],\n): Promise<PushScope> {\n const pushed = refs.filter((ref) => !ZERO.test(ref.localSha));\n if (refs.length > 0 && pushed.length === 0) {\n return { kind: 'empty', why: 'this push only deletes refs' };\n }\n const head = await out(root, ['rev-parse', 'HEAD']);\n const atHead = pushed.find((ref) => ref.localSha === head);\n const others = pushed.filter((ref) => ref !== atHead).map((ref) => ref.localRef);\n if (pushed.length > 0 && atHead === undefined) {\n return {\n kind: 'elsewhere',\n why: `pushing ${others.join(', ')}, but the checkout is at ${short(head ?? '?')}`,\n };\n }\n const ref = atHead ?? {\n localRef: 'HEAD',\n localSha: head ?? 'HEAD',\n remoteRef: '',\n remoteSha: '0'.repeat(40),\n };\n const found = await baseFor(root, remote, ref);\n const also = others.length > 0 ? `; ${others.join(', ')} not checked here` : '';\n if (!('base' in found)) return { kind: 'unscoped', why: `${found.why}${also}` };\n\n const diff = await probe([\n 'git',\n '-C',\n root,\n 'diff',\n '--name-only',\n '-z',\n found.base,\n ref.localSha,\n ]);\n if (diff.code !== 0) return { kind: 'unscoped', why: `git diff ${short(found.base)} failed` };\n const changed = diff.stdout.split('\\0').filter((path) => path !== '');\n if (changed.length === 0) return { kind: 'empty', why: `no file differs ${found.why}${also}` };\n return { kind: 'scoped', base: found.base, changed, why: `${found.why}${also}` };\n}\n\n/**\n * A path that changes what EVERY test runs on: a manifest, a lockfile, Bun's config, a\n * tsconfig. Bun's `--changed` follows imports and none of these is imported — measured\n * 2026-09-23, editing package.json selected 0 of 246 test files — so a push touching one\n * runs the tests in full rather than none of them.\n */\nexport function changesEverything(path: string): boolean {\n const name = path.split('/').pop() ?? '';\n return /^(package\\.json|bun\\.lockb?|bunfig\\.toml|tsconfig.*\\.json)$/.test(name);\n}\n",
11
+ "/**\n * Refuse to commit anything that looks like a credential.\n *\n * ⛔ FIRST GATE, ALWAYS. A secret that reaches a public repository is compromised the moment\n * it is pushed — rotating it is the only remedy, and rewriting history does not help\n * because the object is already fetched and mirrored. Every other check can be fixed\n * after the fact; this one cannot. Moved here from homeflare-kit's own\n * scripts/hooks/secrets.ts (2026-09-23) so every repository commits under it, not one.\n * ★ gitleaks IS THE TOOL, not a hand-rolled regex list. It ships hundreds of maintained rules\n * and an entropy engine, and it reads the repository's own `.gitleaks.toml` when there is\n * one; a homegrown pattern set covers the token shapes its author thought of.\n * ⚠️ IT IS A GO BINARY, NOT AN npm PACKAGE — nothing in package.json can install it, so a\n * missing binary is explained rather than surfacing as \"command not found\".\n * ⚠️ `git --staged`, NOT `protect`. Measured 2026-09-15 on gitleaks 8.30.1: `protect` still\n * runs, but the documented surface is `gitleaks git` / `dir` / `stdin`.\n */\nimport { fail, ok, run } from './report.ts';\n\nconst INSTALL = 'brew install gitleaks — Linux: https://github.com/gitleaks/gitleaks/releases';\n\nexport async function scanStagedSecrets(): Promise<void> {\n // ⛔ NOT A SILENT SKIP. A secret scan that quietly does nothing is worse than none: it\n // reads, in a log and in a reviewer's head, as coverage that does not exist.\n if (Bun.which('gitleaks') === null) {\n fail(\n 'pre-commit',\n 'gitleaks is not installed, so the staged changes were NOT scanned',\n INSTALL,\n );\n }\n // `--redact`, so a real finding never prints the secret into a scrollback, a CI log, or an\n // agent's context.\n const code = await run(['gitleaks', 'git', '--staged', '--redact', '--no-banner', '.']);\n if (code !== 0) {\n fail(\n 'pre-commit',\n 'gitleaks found a secret in the staged changes',\n 'remove it, then ROTATE it — assume anything committed is already compromised',\n );\n }\n ok('pre-commit: gitleaks found no secret in the staged changes');\n}\n",
6
12
  "/**\n * What git has staged, split by what the hook may safely touch.\n *\n * ⛔ A STAGED FILE THAT ALSO HAS UNSTAGED EDITS IS OFF LIMITS. Formatting it in place\n * and running `git add` would sweep the contributor's work-in-progress into a commit\n * they did not ask for — the single worst thing a hook can do. Those files are\n * reported and checked, never rewritten.\n */\nimport { capture } from './report.ts';\n\n/**\n * ⚠️ `.md` IS IN THIS LIST ON PURPOSE. House `oxfmt` formats markdown, so a hook that\n * skipped it would let an unformatted changeset through and CI would fail a commit\n * that looked clean locally. A hook must check what CI checks or it trains distrust.\n */\nconst FORMATTABLE = /\\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md)$/;\nconst CODE = /\\.(ts|tsx|js|jsx|mjs|cjs)$/;\n\nexport type Staged = {\n /** Fully staged and formattable — safe to rewrite and restage. */\n readonly formattable: readonly string[];\n /** The subset of `formattable` that oxlint understands. */\n readonly code: readonly string[];\n /** Staged but also dirty in the worktree — checked, never rewritten. */\n readonly partial: readonly string[];\n};\n\n/**\n * 🔴 `-z`, AND SPLIT ON NUL. Measured 2026-09-22: without it git applies `core.quotePath`\n * and a file named `café .ts` comes back as the literal 12 characters\n * `\"caf\\303\\251 .ts\"` — quotes, backslashes and octal escapes. Passing that to oxfmt\n * names a file that does not exist, so every commit touching it fails with a message\n * about the wrong path, and the fix anyone would reach for is `--no-verify`.\n */\nasync function names(args: readonly string[]): Promise<readonly string[]> {\n const out = await capture(['git', ...args, '-z']);\n return out.split('\\0').filter((line) => line.length > 0);\n}\n\n/** Classify the index. Deletions are excluded — there is nothing to format in them. */\nexport async function staged(): Promise<Staged> {\n const indexed = await names(['diff', '--cached', '--name-only', '--diff-filter=ACMR']);\n const dirty = new Set(await names(['diff', '--name-only', '--diff-filter=ACMR']));\n const candidates = indexed.filter((file) => FORMATTABLE.test(file));\n\n return {\n formattable: candidates.filter((file) => !dirty.has(file)),\n code: candidates.filter((file) => !dirty.has(file) && CODE.test(file)),\n partial: candidates.filter((file) => dirty.has(file)),\n };\n}\n\n/**\n * Content fingerprints, so only the files a formatter actually changed get restaged.\n *\n * ★ WHY NOT RESTAGE EVERYTHING. `git add` on an untouched file is harmless but noisy:\n * the hook would claim it rewrote files it left alone, and a hook that overstates\n * what it did is one nobody reads.\n */\nexport async function fingerprints(files: readonly string[]): Promise<ReadonlyMap<string, string>> {\n const out = new Map<string, string>();\n for (const file of files) {\n const handle = Bun.file(file);\n if (!(await handle.exists())) continue;\n out.set(file, String(Bun.hash(await handle.arrayBuffer())));\n }\n return out;\n}\n",
7
- "/**\n * The two gates every HomeFlare repo gets, from one place.\n *\n * ★ THE SPLIT IS BY COST. `pre-commit` touches only the staged files and is measured in\n * hundreds of milliseconds, so it can run on every commit without anyone resenting\n * it. `pre-push` runs the repo's own `bun run check` — seconds, once, before the\n * change costs a slot on the shared self-hosted runner.\n *\n * ⚠️ NEITHER IS A GATE. Both are skippable with `--no-verify` and neither exists in a\n * fresh clone until `bun install` runs `prepare`. The required checks on `main` stay\n * the gate; these only make the cheap mistakes cheap to find.\n */\nimport { fail, note, ok, run, tool } from './report.ts';\nimport { fingerprints, staged } from './staged.ts';\n\n/**\n * Format and lint what is staged, restaging only what the formatter rewrote.\n *\n * ⚠️ THIS HOOK REWRITES FILES DURING A COMMIT, and it says so on the line where it\n * happens. Without the restage, oxfmt would fix the worktree while the commit kept\n * the unformatted bytes — CI then fails on a file that reads as correct locally.\n */\nexport async function preCommit(root: string): Promise<void> {\n const oxfmt = tool(root, 'oxfmt');\n const { formattable, code, partial } = await staged();\n\n // ⛔ Checked, never rewritten — see staged.ts for why `git add` here would be theft.\n if (partial.length > 0) {\n note(`${partial.length} staged file(s) also have unstaged edits; checking without rewriting`);\n if ((await run([...oxfmt, '--check', ...partial])) !== 0) {\n fail(\n 'pre-commit',\n 'those partially staged files are unformatted, and rewriting them would stage your unstaged work',\n 'bun run format, then stage the files you meant to commit',\n );\n }\n }\n\n if (formattable.length === 0) {\n ok('pre-commit: nothing staged to format');\n return;\n }\n\n const before = await fingerprints(formattable);\n if ((await run([...oxfmt, ...formattable])) !== 0) {\n fail('pre-commit', 'oxfmt could not format the staged files', 'bun run format');\n }\n const after = await fingerprints(formattable);\n const rewritten = formattable.filter((file) => before.get(file) !== after.get(file));\n\n if (rewritten.length > 0) {\n note(`oxfmt rewrote and restaged ${rewritten.length} file(s): ${rewritten.join(', ')}`);\n if ((await run(['git', 'add', '--', ...rewritten])) !== 0) {\n fail('pre-commit', 'could not restage the formatted files', 'git add the listed files');\n }\n }\n\n // ⚠️ `--deny-warnings` matches what CI runs. A hook that is laxer than CI is worse\n // than no hook: it certifies a change CI will reject.\n if (code.length > 0 && (await run([...tool(root, 'oxlint'), '--deny-warnings', ...code])) !== 0) {\n fail(\n 'pre-commit',\n `oxlint found problems in ${code.length} staged file(s)`,\n 'bun run lint:fix, then fix by hand what remains',\n );\n }\n\n ok(`pre-commit: ${formattable.length} staged file(s) formatted and linted`);\n}\n\n/**\n * Run the repo's own declared gate before the push reaches the runner.\n *\n * ★ IT CALLS `bun run check` RATHER THAN NAMING TOOLS. Every repo's `check` is the\n * command CI runs; hard-coding `tsc` and `bun test` here would drift from whichever\n * repo added a step, and the hook would certify a push CI rejects.\n * ⛔ It does not widen a narrow `check`. If a repo's gate only looks at part of the\n * tree, this hook inherits exactly that blind spot — fix the script, not the hook.\n */\nexport async function prePush(root: string): Promise<void> {\n const manifest = Bun.file(`${root}/package.json`);\n const pkg = (await manifest.exists())\n ? ((await manifest.json()) as { scripts?: Record<string, string> })\n : {};\n\n if (pkg.scripts?.['check'] === undefined) {\n note('pre-push: no `check` script declared in package.json; nothing to run');\n return;\n }\n\n note('pre-push: running `bun run check` — the same gate CI runs');\n const started = Bun.nanoseconds();\n // 🔴 `isolated`: strip the GIT_* this hook inherited before running the test suite.\n // See report.ts — without it a test's throwaway git repository commits into this one.\n if ((await run(['bun', 'run', 'check'], true)) !== 0) {\n fail(\n 'pre-push',\n 'bun run check failed — CI would fail the same way, on a shared runner',\n 'bun run lint:fix, then bun run check until it is green',\n );\n }\n\n ok(`pre-push: bun run check passed in ${((Bun.nanoseconds() - started) / 1e9).toFixed(1)}s`);\n}\n",
8
- "/**\n * Adoption: the one wrapper every repo commits, and the check that it has not drifted.\n *\n * ★ WHY A WRAPPER AT ALL. husky can only run a file that is tracked in the repo, so\n * something must be committed per repo. This keeps that something to a delegation\n * whose text is owned HERE — the behaviour lives in one package, and a repo that\n * edits its copy is reported as drift rather than quietly diverging.\n * ★ ONE FILE, TWO NAMES. The wrapper reads the hook name from `$0`, so `pre-commit`\n * and `pre-push` are byte-identical and there is a single text to keep in step.\n */\nimport { chmod, mkdir } from 'node:fs/promises';\n\n/** The hook files this package installs, in the order a contributor meets them. */\nexport const HOOK_NAMES = ['pre-commit', 'pre-push'] as const;\n\n/** Where the runner lives once `bun install` has run. Relative: git runs hooks at the root. */\nconst RUNNER = 'node_modules/@homeflare/config/bin/hooks.ts';\n\n/**\n * ⚠️ IT EXITS 0 WHEN THE RUNNER IS ABSENT. A checkout with no `node_modules` would\n * otherwise fail every commit with a module-resolution error, and the first thing\n * anyone would do is delete the hook. Failing open is the right trade for a\n * convenience; the required checks on `main` are what must fail closed.\n */\nexport const HUSKY_HOOK: string = `# HomeFlare shared git hook. The behaviour lives in @homeflare/config, not in this file,\n# and the same bytes are installed as .husky/pre-commit and .husky/pre-push — the hook\n# name comes from $0.\n#\n# ⚠️ A hook is a local convenience, not a gate: it is skippable with --no-verify and does\n# not exist in a fresh clone until \\`bun install\\` runs the \\`prepare\\` script. The\n# required checks on main stay the gate.\n#\n# Regenerate this file with: bun ${RUNNER} install\nhook=\"${RUNNER}\"\nif [ ! -f \"$hook\" ]; then\n echo \"husky: $hook is missing — run 'bun install' to enable the HomeFlare hooks; skipping\"\n exit 0\nfi\nexec bun \"$hook\" \"$(basename \"$0\")\"\n`;\n\n/** Write the wrapper into `.husky/`. Returns the paths written, relative to the project. */\nexport async function installHooks(projectDir: string): Promise<readonly string[]> {\n await mkdir(`${projectDir}/.husky`, { recursive: true });\n const written: string[] = [];\n for (const name of HOOK_NAMES) {\n const path = `${projectDir}/.husky/${name}`;\n await Bun.write(path, HUSKY_HOOK);\n // ⚠️ husky's own runner does `sh -e \"$s\"`, which does not need the execute bit, but\n // `core.hooksPath=.husky` without husky does. Set it so both mechanisms work.\n await chmod(path, 0o755);\n written.push(`.husky/${name}`);\n }\n return written;\n}\n\n/**\n * Report what stops this project's hooks from working. Empty means adopted.\n *\n * ⛔ DELIBERATELY NOT PART OF `checkProject`. Every repo in the estate runs that checker\n * from a test; folding hook conformance into it would turn every repo that has not\n * adopted yet red on `main` in the same commit. A repo opts in by calling this.\n */\nexport async function problemsInHooks(projectDir: string): Promise<readonly string[]> {\n const problems: string[] = [];\n const manifest = Bun.file(`${projectDir}/package.json`);\n\n if (!(await manifest.exists())) return ['package.json: missing'];\n const pkg = (await manifest.json()) as {\n scripts?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n if (!(pkg.scripts?.['prepare'] ?? '').includes('husky')) {\n problems.push('package.json: no \"prepare\": \"husky\" script — a fresh clone installs no hooks');\n }\n if (pkg.devDependencies?.['husky'] === undefined) {\n problems.push('package.json: husky is not a devDependency');\n }\n\n for (const name of HOOK_NAMES) {\n const file = Bun.file(`${projectDir}/.husky/${name}`);\n if (!(await file.exists())) {\n problems.push(`.husky/${name}: missing — run \\`bun ${RUNNER} install\\``);\n continue;\n }\n if ((await file.text()) !== HUSKY_HOOK) {\n problems.push(\n `.husky/${name}: differs from the @homeflare/config wrapper — run \\`bun ${RUNNER} install\\`, or change it in the package`,\n );\n }\n }\n\n return problems;\n}\n",
9
- "/**\n * The HomeFlare git hooks, as a package.\n *\n * ★ WHY THIS IS NOT A SCRIPT IN EVERY REPO. Fourteen copies of a hook script is the\n * exact drift `@homeflare/config` exists to prevent: the copies diverge, nobody\n * notices, and two repos disagree about what a commit must satisfy. Here the repo\n * commits a delegating wrapper and the behaviour ships with the package, so changing\n * the rule is one release and a version bump rather than fourteen edits.\n *\n * ⚠️ HUSKY, NOT lefthook OR A BARE `core.hooksPath`. It is already the estate's\n * mechanism in the repos that have working hooks, it installs from `prepare` on a\n * plain `bun install`, and it keeps the hook files tracked and reviewable. A second\n * mechanism alongside it would mean two ways to answer \"are hooks on in this repo\".\n *\n * Usage from a hook file — see `HUSKY_HOOK`:\n *\n * bun node_modules/@homeflare/config/bin/hooks.ts pre-commit\n */\nimport { preCommit, prePush } from './hooks/gates.ts';\nimport { HOOK_NAMES, HUSKY_HOOK, installHooks, problemsInHooks } from './hooks/install.ts';\nimport { type Hook, note, ok } from './hooks/report.ts';\n\nexport { HOOK_NAMES, HUSKY_HOOK, installHooks, problemsInHooks };\nexport type { Hook };\n\n/** The commands `bin/hooks.ts` accepts. */\nexport type Command = Hook | 'install';\n\nexport function isCommand(value: string): value is Command {\n return value === 'pre-commit' || value === 'pre-push' || value === 'install';\n}\n\n/**\n * Run one hook, or install the wrappers.\n *\n * ⛔ Never exits non-zero for a reason the caller cannot act on: an unknown command is a\n * programming error in the wrapper and is reported as such, not as a failed commit.\n */\nexport async function runCommand(command: Command, root: string): Promise<void> {\n if (command === 'install') {\n const written = await installHooks(root);\n ok(`wrote ${written.join(', ')} — commit them`);\n note('they do nothing until `bun install` runs `prepare` (husky)');\n return;\n }\n if (command === 'pre-commit') return await preCommit(root);\n return await prePush(root);\n}\n"
13
+ "/**\n * Adoption: the one wrapper every repo commits, and the check that it has not drifted.\n *\n * ★ WHY A WRAPPER AT ALL. git can only run a file that is present in the worktree, so\n * something must be committed per repo. This keeps that something to a delegation\n * whose text is owned HERE — the behaviour lives in one package, and a repo that\n * edits its copy is reported as drift rather than quietly diverging.\n * ★ ONE FILE, TWO NAMES. The wrapper reads the hook name from `$0`, so `pre-commit`\n * and `pre-push` are byte-identical and there is a single text to keep in step.\n * ★ THE DIRECTORY KEEPS HUSKY'S NAME, NOT HUSKY. `.husky/` is where the estate's hook files\n * already live (homeflare-kit, homeflare-alerts, the house monorepo); since 2026-09-23 git\n * runs them directly through `core.hooksPath` — see activate.ts for why husky's own\n * `.husky/_` left every fresh worktree without hooks.\n */\nimport { chmod, mkdir, stat } from 'node:fs/promises';\n\n/** The hook files this package installs, in the order a contributor meets them. */\nexport const HOOK_NAMES = ['pre-commit', 'pre-push'] as const;\n\n/** Where the runner lives once `bun install` has run. Relative: git runs hooks at the root. */\nconst RUNNER = 'node_modules/@homeflare/config/bin/hooks.ts';\n\n/** What a consumer's `prepare` script runs, so every `bun install` activates the hooks. */\nexport const PREPARE: string = `bun ${RUNNER} activate`;\n\n/**\n * ⚠️ IT EXITS 0 WHEN THE RUNNER IS ABSENT. A worktree with no `node_modules` would\n * otherwise fail every commit with a module-resolution error, and the first thing\n * anyone would do is delete the hook. Failing open is the right trade for a\n * convenience; the required checks on `main` are what must fail closed.\n * ★ `\"$@\"` AND STDIN PASS THROUGH. `pre-push` reads the remote name from its first argument\n * and the pushed refs from stdin (push-range.ts); `exec` keeps both.\n * ⚠️ NO SHEBANG, AND THAT IS MEASURED, NOT FORGOTTEN: git 2.55 runs an executable hook that\n * has none through `sh` (2026-09-23), and the husky-era files in the estate have none.\n */\nexport const HUSKY_HOOK: string = `# HomeFlare shared git hook. The behaviour lives in @homeflare/config, not in this file,\n# and the same bytes are installed as .husky/pre-commit and .husky/pre-push — the hook\n# name comes from $0, and git's arguments and stdin pass straight through.\n#\n# ⚠️ A hook is a local convenience, not a gate: it is skippable with --no-verify, and a\n# worktree runs it only once \\`bun install\\` has run there. The required checks on main\n# stay the gate.\n#\n# Regenerate this file with: bun ${RUNNER} install\nhook=\"${RUNNER}\"\nif [ ! -f \"$hook\" ]; then\n echo \"homeflare hooks: $hook is missing — run 'bun install' in this worktree; skipping\" >&2\n exit 0\nfi\nexec bun \"$hook\" \"$(basename \"$0\")\" \"$@\"\n`;\n\n/** Write the wrapper into `.husky/`. Returns the paths written, relative to the project. */\nexport async function installHooks(projectDir: string): Promise<readonly string[]> {\n await mkdir(`${projectDir}/.husky`, { recursive: true });\n const written: string[] = [];\n for (const name of HOOK_NAMES) {\n const path = `${projectDir}/.husky/${name}`;\n await Bun.write(path, HUSKY_HOOK);\n // ⛔ THE EXECUTE BIT IS REQUIRED. git runs a `core.hooksPath` file directly and IGNORES\n // a non-executable hook, with nothing but an advice line to say so.\n await chmod(path, 0o755);\n written.push(`.husky/${name}`);\n }\n return written;\n}\n\n/**\n * Report what stops this project's hooks from working. Empty means adopted.\n *\n * ⛔ DELIBERATELY NOT PART OF `checkProject`. Every repo in the estate runs that checker\n * from a test; folding hook conformance into it would turn every repo that has not\n * adopted yet red on `main` in the same commit. A repo opts in by calling this.\n */\nexport async function problemsInHooks(projectDir: string): Promise<readonly string[]> {\n const problems: string[] = [];\n const manifest = Bun.file(`${projectDir}/package.json`);\n\n if (!(await manifest.exists())) return ['package.json: missing'];\n const pkg = (await manifest.json()) as { scripts?: Record<string, string> };\n\n const prepare = pkg.scripts?.['prepare'] ?? '';\n if (!/bin\\/hooks\\.ts activate/.test(prepare)) {\n problems.push(\n `package.json: \"prepare\" does not run \\`${PREPARE}\\` — no clone or worktree gets hooks`,\n );\n }\n // ⚠️ husky POINTS core.hooksPath AT AN UNTRACKED `.husky/_` that exists only where it ran —\n // the gap activate.ts closes. Left in `prepare`, it undoes the activation.\n if (/\\bhusky\\b/.test(prepare)) {\n problems.push(\n 'package.json: \"prepare\" still runs husky, which re-points core.hooksPath at .husky/_',\n );\n }\n\n for (const name of HOOK_NAMES) {\n const path = `${projectDir}/.husky/${name}`;\n const file = Bun.file(path);\n if (!(await file.exists())) {\n problems.push(`.husky/${name}: missing — run \\`bun ${RUNNER} install\\``);\n continue;\n }\n if ((await file.text()) !== HUSKY_HOOK) {\n problems.push(\n `.husky/${name}: differs from the @homeflare/config wrapper — run \\`bun ${RUNNER} install\\`, or change it in the package`,\n );\n }\n if (((await stat(path)).mode & 0o111) === 0) {\n problems.push(`.husky/${name}: not executable, so git ignores it — chmod +x it and commit`);\n }\n }\n\n return problems;\n}\n",
14
+ "/**\n * The HomeFlare git hooks, as a package.\n *\n * ★ WHY THIS IS NOT A SCRIPT IN EVERY REPO. Fourteen copies of a hook script is the\n * exact drift `@homeflare/config` exists to prevent: the copies diverge, nobody\n * notices, and two repos disagree about what a commit must satisfy. Here the repo\n * commits a delegating wrapper and the behaviour ships with the package, so changing\n * the rule is one release and a version bump rather than fourteen edits.\n * ★ TRACKED `.husky/`, RUN BY GIT THROUGH `core.hooksPath` — NOT husky's `.husky/_`. Since\n * 2026-09-23 the mechanism is the one that reaches every worktree; activate.ts has the\n * measurement. The files keep the directory name the estate already uses.\n * ★ PRE-PUSH IS SCOPED TO THE PUSH (push-range.ts, push-plan.ts): the repository's own\n * `check`, with `bun test` narrowed to the tests the pushed files can reach and the build\n * and smoke test left to CI. Many agents push to many repositories at once; a pre-push\n * that re-ran every suite was the one people learned to skip.\n *\n * Usage from a hook file — see `HUSKY_HOOK`:\n *\n * bun node_modules/@homeflare/config/bin/hooks.ts pre-commit\n */\nimport { activateHooks } from './hooks/activate.ts';\nimport { preCommit, prePush } from './hooks/gates.ts';\nimport { HOOK_NAMES, HUSKY_HOOK, PREPARE, installHooks, problemsInHooks } from './hooks/install.ts';\nimport { type Lane, planLanes } from './hooks/push-plan.ts';\nimport { type Hook, note, ok } from './hooks/report.ts';\n\nexport { HOOK_NAMES, HUSKY_HOOK, PREPARE, activateHooks, installHooks, planLanes, problemsInHooks };\nexport type { Hook, Lane };\n\n/** The commands `bin/hooks.ts` accepts. */\nexport type Command = Hook | 'install' | 'activate';\n\nexport function isCommand(value: string): value is Command {\n return ['pre-commit', 'pre-push', 'install', 'activate'].includes(value);\n}\n\n/**\n * Run one hook, write the wrappers, or activate them.\n *\n * `args` are git's own hook arguments (for `pre-push`: the remote name and URL) and\n * `stdin` is what git wrote to the hook (for `pre-push`: the refs being pushed).\n * ⛔ Never exits non-zero for a reason the caller cannot act on: an unknown command is a\n * programming error in the wrapper and is reported as such, not as a failed commit.\n */\nexport async function runCommand(\n command: Command,\n root: string,\n args: readonly string[] = [],\n stdin = '',\n): Promise<void> {\n if (command === 'install') {\n const written = await installHooks(root);\n ok(`wrote ${written.join(', ')} — commit them`);\n note(`then set \"prepare\": \"${PREPARE}\" so every install activates them`);\n return;\n }\n if (command === 'activate') {\n const result = await activateHooks(root);\n (result.active ? ok : note)(`homeflare hooks: ${result.message}`);\n return;\n }\n if (command === 'pre-commit') return await preCommit(root);\n return await prePush(root, args, stdin);\n}\n"
10
15
  ],
11
- "mappings": "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",
12
- "debugId": "3480C5F48A0E1AD864756E2164756E21",
16
+ "mappings": "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",
17
+ "debugId": "F6FB90E426FFDF3C64756E2164756E21",
13
18
  "names": []
14
19
  }
@@ -6,11 +6,28 @@
6
6
  * this house are the product. The rendered workflows are written as text with holes;
7
7
  * only the step lists, whose shape varies per repository, go through here.
8
8
  *
9
- * ⚠️ Bun can PARSE YAML natively (`Bun.YAML.parse`) but does not stringify it, measured
10
- * against Bun 1.4.0 on 2026-09-22. The tests parse what this writes with `Bun.YAML.parse`
11
- * and compare structures, so a malformed emission fails rather than shipping.
9
+ * ⚠️ Bun.YAML.stringify EXISTS (`Object.keys(Bun.YAML)` is `["parse", "stringify"]`,
10
+ * measured against Bun 1.4.0 on 2026-09-23) but its output does not fit these rules:
11
+ * (a) a multi-line `run:` comes out as a double-quoted string with `\n` escapes, e.g.
12
+ * `run: "echo a\necho b\n"`, not a `|` block scalar; (b) `09:00` comes out UNQUOTED,
13
+ * e.g. `cron: 09:00`, the YAML 1.1 sexagesimal trap the `scalar()` comment below guards
14
+ * against; (c) a mapping key is followed by a trailing space, e.g. `"steps: \n - ..."`;
15
+ * and (d) a plain JS object has nowhere to attach a comment, so it cannot carry one. The
16
+ * tests parse what this writes with `Bun.YAML.parse` and compare structures, so a
17
+ * malformed emission fails rather than shipping.
12
18
  */
13
19
  import type { JobStep } from './shape.ts';
20
+ /**
21
+ * Trim trailing `\n` characters the way `command.replace(/\n+$/, '')` used to, but
22
+ * linear instead of backtracking — the same pattern as `normalizeBaseUrl` in
23
+ * `packages/distilled-netbox/src/credentials.ts`. Exported so a test can compare it
24
+ * against the old regex directly.
25
+ *
26
+ * ⚠️ Linear on purpose: a `/\n+$/` regex backtracks polynomially on a long run of
27
+ * "\n" that is not at the end (CodeQL js/polynomial-redos), and `command` here is
28
+ * repository-configured step text.
29
+ */
30
+ export declare function trimTrailingNewlines(value: string): string;
14
31
  /** One step, as the lines of a `steps:` list item at `depth`. */
15
32
  export declare function renderStep(step: JobStep, depth: number): string[];
16
33
  /** A whole `steps:` list, already indented for a job at `depth`. */
@@ -1 +1 @@
1
- {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/repo-shape/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAsD1C,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA2BjE;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5E"}
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/repo-shape/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AA2C1C;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI1D;AAaD,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA2BjE;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5E"}
@@ -191,8 +191,14 @@ function scalar(value) {
191
191
  function renderMapping(entries, depth) {
192
192
  return Object.entries(entries).map(([key, value]) => `${indent(depth)}${key}: ${scalar(value)}`);
193
193
  }
194
+ function trimTrailingNewlines(value) {
195
+ let end = value.length;
196
+ while (end > 0 && value.charCodeAt(end - 1) === 10)
197
+ end--;
198
+ return value.slice(0, end);
199
+ }
194
200
  function renderRun(command, depth) {
195
- const lines = command.replace(/\n+$/, "").split(`
201
+ const lines = trimTrailingNewlines(command).split(`
196
202
  `);
197
203
  if (lines.length === 1)
198
204
  return [`${indent(depth)}run: ${scalar(lines[0] ?? "")}`];
@@ -775,5 +781,5 @@ export {
775
781
  runsOn
776
782
  };
777
783
 
778
- //# debugId=2943BDDA9944E35364756E2164756E21
784
+ //# debugId=C9A5F54A65373F0A64756E2164756E21
779
785
  //# sourceMappingURL=repo-shape.js.map
@@ -5,7 +5,7 @@
5
5
  "/**\n * `.github/dependabot.yml`, rendered — and the one group that carries kit releases.\n *\n * ★ MEASURED BEFORE IT WAS RENDERED (2026-09-22): 1 of 14 repositories had a Dependabot\n * config. Twelve took no dependency or Action updates at all, and nothing said so.\n *\n * ★ THE `homeflare` GROUP IS HOW A KIT RELEASE REACHES A CONSUMER (Tim, 2026-09-23:\n * \"Dependabot, grouped\", checked daily, merged on green). Measured the same day, the\n * leg nobody automated had drifted: proxmox and mini pinned `@homeflare/alchemy` 0.13.0\n * while the kit had published 0.19.1. `automerge.ts` renders the workflow that arms\n * auto-merge on this group's pull request and on nothing else.\n *\n * Every Dependabot claim below was read from GitHub's docs or dependabot-core's source on\n * 2026-09-22 (dependabot-core v0.397.0); `docs/repo-shape-dependabot.md` has the citations.\n */\nimport type { RepoShape } from './shape.ts';\n\n/**\n * The group's identifier. ⛔ IT IS ALSO HALF OF A BRANCH NAME the auto-merge workflow\n * matches — dependabot-core names a group's branch `dependabot/bun/<group>-<10 hex>`\n * (branch_namer/dependency_group_strategy.rb) — so it is exported, not retyped there.\n * Dependabot requires an identifier that starts and ends with a letter.\n */\nexport const HOMEFLARE_GROUP = 'homeflare';\n\n/** The first-party scope. Every package the kit publishes is under it. */\nexport const HOMEFLARE_PATTERN = '@homeflare/*';\n\n/**\n * ★ SEVEN DAYS FOR THIRD-PARTY VERSIONS. The bun block has to run daily for the kit's sake\n * (see below), so the weekly pace the estate had for everything else is kept by age\n * instead of by calendar: a third-party release is proposed once it is a week old. That\n * is also the supply-chain half — a compromised release is usually yanked within days.\n */\nexport const THIRD_PARTY_COOLDOWN_DAYS = 7;\n\n/** `.github/dependabot.yml`. */\nexport function renderDependabot(shape: RepoShape): string {\n // ⚠️ `directories`, not `directory`, for bun: a workspace keeps a dependency in the\n // package that declares it, so pointing only at `/` leaves every `packages/*`\n // manifest unwatched — and the symptom is silence, not an error.\n const bunDirs = shape.publishes ? ['/', '/packages/*'] : ['/'];\n return `# Dependabot for ${shape.repository}.\n#\n# 🤖 RENDERED BY @homeflare/config — DO NOT EDIT THIS FILE BY HAND.\n# Refresh with \\`bun run repo-shape:refresh\\`.\n#\n# ⛔ THIS FILE LIVES AT .github/dependabot.yml, NOT IN .github/workflows/. Dependabot is a\n# platform feature, not an Action — a config placed among the workflows is silently\n# ignored, and the symptom is simply that no pull requests ever arrive. Measured\n# 2026-09-22: 13 of 14 HomeFlare repositories had no dependabot config at all, so their\n# Actions and their toolchain went stale invisibly, which is exactly how nothing fails.\n#\n# ★ WHY GROUPED RATHER THAN ONE PR PER DEPENDENCY. The default opens a pull request per\n# outdated package; that is a trickle nobody reviews properly. Each group below is a set\n# that is either safe to take together or needs deciding together.\n#\n# ★ THE BILLING LOCK DOES NOT STOP THIS FILE. Dependabot runs as a GitHub-hosted \"dynamic\"\n# workflow that GitHub's docs say \"does not count towards your included GitHub Actions\n# minutes\"; measured 2026-09-22, a private estate repository's Dependabot job got a hosted\n# runner 86 minutes after that account's ordinary hosted jobs were refused for billing.\n# It cannot use the mini anyway: a self-hosted Dependabot runner must be Linux x64 with\n# Docker, and the mini's runners are arm64 containers with no Docker socket.\nversion: 2\n\nupdates:\n # ── The toolchain (bun.lock) ────────────────────────────────────────────────\n # ⚠️ BLOCKED UPSTREAM, 2026-09-22: bun 1.4 writes bun.lock \\`lockfileVersion\\` 2 and\n # Dependabot's updater bundles bun 1.3.14, which reads up to 1, so this block fails in\n # every estate repository with \"Unsupported bun.lock 'lockfileVersion' 2\". The fix is\n # dependabot/dependabot-core pull request 16071. The github-actions block is unaffected.\n # ⛔ ONE BUN BLOCK, SO ONE SCHEDULE. Dependabot refuses two blocks for one ecosystem and\n # target branch whose directories overlap, so the \\`homeflare\\` group cannot be daily\n # while the rest stays weekly. The block is daily; \\`cooldown\\` slows the rest.\n - package-ecosystem: bun\n directories:\n${bunDirs.map((dir) => ` - ${dir}`).join('\\n')}\n schedule:\n # ⚠️ Dependabot's \\`daily\\` is Monday to Friday; a weekend kit release lands on Monday.\n interval: daily\n time: '09:00'\n timezone: America/New_York\n # ⛔ THE EXCLUDE IS NOT OPTIONAL. Dependabot applies a 3-day cooldown to every version\n # update even when this key is absent, so without it a kit release would wait three\n # days before its bump opened — and \"daily\" would quietly mean \"three days late\".\n cooldown:\n default-days: ${THIRD_PARTY_COOLDOWN_DAYS}\n exclude: ['${HOMEFLARE_PATTERN}']\n open-pull-requests-limit: 5\n commit-message:\n prefix: 'chore'\n include: scope\n labels: [dependencies]\n groups:\n # ★ FIRST, AND EVERY UPDATE TYPE. A kit release is one set of packages built to work\n # together, so they move as one pull request; \\`bun run check\\` is what reads it, and\n # .github/workflows/dependabot-automerge.yml merges it when that is green.\n # ⚠️ A release that changes what @homeflare/config renders fails the drift test here\n # by design; \\`bun run repo-shape:refresh\\` on the branch is the one-command fix.\n ${HOMEFLARE_GROUP}:\n patterns: ['${HOMEFLARE_PATTERN}']\n\n # oxfmt and oxlint move together and only affect style. Minor and patch bumps are\n # noise unless they fail CI, which is what CI is for.\n lint-and-format:\n patterns: ['oxfmt', 'oxlint']\n update-types: [minor, patch]\n\n # ⚠️ MAJORS EXCLUDED DELIBERATELY. TypeScript majors change what typechecks;\n # changesets majors have renamed inputs and dropped compatibility (the action's v2\n # did both). These want reading, not merging on green.\n build-tooling:\n patterns: ['typescript', '@changesets/*', '@types/bun']\n update-types: [minor, patch]\n\n # ── The workflows themselves ────────────────────────────────────────────────\n # ★ Actions go stale invisibly: nothing fails, they just keep running old code. The\n # estate's first workflows pinned checkout@v5 (current: v7) and changesets/action@v1\n # (current: v2, with every input renamed) — measured 2026-09-15.\n - package-ecosystem: github-actions\n directory: /\n schedule:\n interval: weekly\n day: monday\n time: '09:00'\n timezone: America/New_York\n open-pull-requests-limit: 5\n commit-message:\n prefix: 'ci'\n labels: [dependencies, github-actions]\n groups:\n actions:\n patterns: ['*']\n update-types: [minor, patch]\n`;\n}\n",
6
6
  "/**\n * The runtime guards `repo-shape` needs and its types cannot express.\n *\n * ★ EXTRACTED FROM `shape.ts`, NOT INVENTED HERE. The types carry one half of the\n * contract — `Stated<R>` refuses an empty or computed reason at compile time — and\n * these carry the other half: the cases a literal type still lets through. Keeping\n * them in one file is what lets `shape.ts` stay the declaration and nothing else.\n *\n * ⚠️ EVERY MESSAGE STARTS `repo-shape:` AND NAMES THE FIELD. These throw at render or at\n * declaration, far from the workflow file they would otherwise break at job time, so\n * the message is the only thing pointing back at the call that caused it.\n */\n\nconst SENTENCE = 12;\n\nexport function requireNonEmpty(field: string, value: string): string {\n const trimmed = value.trim();\n if (trimmed.length === 0) throw new Error(`repo-shape: ${field} must not be blank`);\n return trimmed;\n}\n\nexport function requireSentence(field: string, value: string): string {\n const trimmed = value.trim();\n // ⚠️ THE TYPE CANNOT CATCH `reason: ' '`. `' '` is a non-empty literal, so `Stated`\n // lets it through and only this does not. Type and guard cover different halves.\n if (trimmed.length < SENTENCE) {\n throw new Error(`repo-shape: ${field} must be a sentence, got ${JSON.stringify(value)}`);\n }\n return trimmed;\n}\n\nexport function requireIsoDate(value: string): string {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n throw new Error(`repo-shape: since must be YYYY-MM-DD, got ${JSON.stringify(value)}`);\n }\n return value;\n}\n\n/**\n * ⛔ A MAJOR, NOT A RANGE, AND NOT A FLOAT. `actions/setup-node` takes `node-version: 24`\n * and resolves the newest 24.x; a fractional or negative value renders YAML the action\n * accepts and then fails to resolve, mid-job, on the runner.\n */\nexport function requireMajor(value: number): number {\n if (!Number.isInteger(value) || value <= 0) {\n throw new Error(`repo-shape: node must be a positive integer major, got ${value}`);\n }\n return value;\n}\n\n/** `timeout-minutes` must be a positive whole number of minutes. */\nexport function requireMinutes(value: number): number {\n if (!Number.isInteger(value) || value <= 0) {\n throw new Error(`repo-shape: timeout must be a positive whole minute count, got ${value}`);\n }\n return value;\n}\n\nexport function requireJobId(value: string): string {\n // ⛔ The id becomes a YAML key and a `needs:` entry. Anything else renders a workflow\n // GitHub rejects at parse time, which reports as \"workflow file issue\" with no line.\n if (!/^[a-z][a-z0-9_-]*$/.test(value)) {\n throw new Error(\n `repo-shape: job id must match /^[a-z][a-z0-9_-]*$/, got ${JSON.stringify(value)}`,\n );\n }\n return value;\n}\n",
7
7
  "/**\n * `RepoShape` — one HomeFlare repository's tooling, as a value.\n *\n * ★ WHY THIS TYPE EXISTS. Measured across the estate on 2026-09-22: 13 repositories each\n * carry a hand-written `.github/workflows/ci.yml`, `security.yml`, `.github/actionlint.yaml`\n * and `.changeset/config.json`. The changeset configs were byte-identical apart from the\n * repository name in 11 of 13. The security workflows were not: five still carried\n * `push: branches: [main]` that the other eight had removed, five were missing the\n * `concurrency:` block entirely, two were missing `pull-requests: read` (whose absence\n * makes every pull-request scan fail 403), the gitleaks action was pinned at `@v2` in\n * five and `@v3` in eight, and the weekly cron minute took three different values. Not\n * one of those differences was a decision. They are what happens when the same edit is\n * applied by hand thirteen times.\n *\n * ⛔ SO: A DIFFERENCE IS EITHER AN INPUT OR AN EXCEPTION. Nothing else. A repository that\n * needs something the standard does not give it either widens this type — and every\n * repository gets the widening — or declares an exception with a stated reason. There\n * is no third option where a file is quietly edited, because `drift.ts` fails on it.\n *\n * ⚠️ THE REASON IS ENFORCED BY THE COMPILER, NOT BY REVIEW. `except()` and `extraJob()`\n * below refuse an empty reason and refuse a reason read out of a variable, so the text\n * has to be written at the exception. See `Stated`.\n */\n\nimport {\n requireIsoDate,\n requireJobId,\n requireMajor,\n requireMinutes,\n requireNonEmpty,\n requireSentence,\n} from './guards.ts';\n\n/** Where a repository's jobs run. */\nexport type RepoRunner =\n /**\n * `[self-hosted, homeflare-mini]` — the interim Linux arm64 runner on the Mac mini.\n * ⚠️ GitHub-hosted runners are refused for this account (billing lock, 2026-09-22).\n */\n | 'mini'\n /** `ubuntu-latest`. Public repositories, where GitHub-hosted minutes are free. */\n | 'github';\n\n/** One step in a rendered job. Either a `uses:` or a `run:`, never both. */\nexport type JobStep = {\n readonly name?: string;\n /**\n * The step's `if:` condition, verbatim.\n *\n * ★ WIDENED RATHER THAN EXCEPTED, 2026-09-22. homeflare-kit's `consumer smoke test`\n * ends with a step that renders the packed tarball sizes onto the run summary, and\n * it carries `if: always()` on purpose — a FAILED smoke test is exactly when the\n * sizes are worth reading. Without this field kit had two options and both were\n * worse: drop the condition, losing the summary on the only runs that need it, or\n * `except('.github/workflows/ci.yml')`, which buys one repository its file back and\n * costs the estate the guarantee on it. One optional key gives every repository the\n * same freedom, which is the order of preference this module documents.\n * ⚠️ NOT VALIDATED. GitHub's expression grammar is the vendor's; a bad condition is\n * caught by `actionlint` in the `workflow lint` job, which is where it belongs.\n */\n readonly if?: string;\n readonly env?: Readonly<Record<string, string>>;\n} & (\n | {\n readonly uses: string;\n readonly with?: Readonly<Record<string, string | number>>;\n readonly run?: never;\n }\n | { readonly run: string; readonly uses?: never; readonly with?: never }\n);\n\n/**\n * ⛔ A REASON MUST BE WRITTEN, NOT COMPUTED. `'' extends R` is true for the empty literal\n * AND for the wide `string` type, so both collapse to `never` and fail to typecheck:\n * `reason: ''` is refused, and so is `reason: someVariable`. Only a string literal\n * written at the call site survives — which is the whole point, because a reason\n * assembled at runtime is a reason nobody reads in the diff.\n * Verified against TypeScript 7.0.2 on 2026-09-22; `tests/repo-shape-reason.test.ts`\n * compiles the refusals and asserts they still fail.\n */\nexport type Stated<R extends string> = '' extends R ? never : R;\n\n/** Paths this package renders. A deviation names one of these, not an arbitrary file. */\nexport type RenderedPath =\n | '.changeset/config.json'\n | '.github/actionlint.yaml'\n | '.github/dependabot.yml'\n | '.github/workflows/ci.yml'\n | '.github/workflows/dependabot-automerge.yml'\n | '.github/workflows/security.yml';\n\nexport interface RepoShapeException {\n /** The rendered file this repository does not take from the renderer. */\n readonly file: RenderedPath;\n /** Why — written at the exception, in a sentence a stranger can act on. */\n readonly reason: string;\n /** `YYYY-MM-DD` the exception was taken, so a stale one is visible. */\n readonly since: string;\n}\n\ninterface ExceptionInput {\n readonly file: RenderedPath;\n readonly reason: string;\n readonly since: string;\n}\n\n/**\n * Declare that this repository keeps its own copy of a rendered file.\n *\n * except({\n * file: '.github/workflows/ci.yml',\n * reason: 'Payload needs Node >=24.15, which the rendered job does not install',\n * since: '2026-09-22',\n * })\n *\n * ⛔ THE DRIFT CHECK STOPS CHECKING THAT FILE. That is the trade: an exception buys the\n * freedom to hand-edit one file and pays for it by losing the guarantee on that file.\n * Prefer widening `RepoShape` — then every repository benefits and nothing is lost.\n */\nexport function except<const E extends ExceptionInput>(\n exception: E & { readonly reason: Stated<E['reason']> },\n): RepoShapeException {\n return {\n file: exception.file,\n reason: requireSentence('reason', exception.reason),\n since: requireIsoDate(exception.since),\n };\n}\n\nexport interface ExtraJob {\n /** The job key in `jobs:`, e.g. `package`. Lower-case, dashes allowed. */\n readonly id: string;\n /**\n * The job's `name:`, which is also the status-check context. It joins the `ci`\n * aggregate's `needs`, so the branch ruleset still requires exactly one check.\n */\n readonly name: string;\n /** Why this repository has a job the other twelve do not. */\n readonly reason: string;\n /** Steps after checkout. The prologue (checkout, bun, install) is rendered for you. */\n readonly steps: readonly JobStep[];\n /** Job ids this one waits for. Default: none, so it runs beside `check`. */\n readonly needs?: readonly string[];\n /** `false` skips the rendered bun prologue — for a job that needs another toolchain. */\n readonly bun?: boolean;\n /**\n * `timeout-minutes:` for this job. Omitted takes GitHub's 360-minute default.\n *\n * ⚠️ AN INPUT BECAUSE A HUNG JOB IS NOT A FAILED JOB. Measured 2026-09-22:\n * homeflare-blog's `runtime` job drives Playwright against a local workerd, and a\n * browser that never reaches its first paint holds a self-hosted slot for six hours\n * rather than reporting red. On a 3-slot pool that is the whole pool. Only a job\n * that starts something with its own wait — a browser, a server, a container — needs\n * this; `check` does not, because `bun run check` exits.\n */\n readonly timeout?: number;\n}\n\ninterface ExtraJobInput extends Omit<ExtraJob, 'needs' | 'bun' | 'timeout'> {\n readonly needs?: readonly string[];\n readonly bun?: boolean;\n readonly timeout?: number;\n}\n\n/**\n * Declare a job this repository needs and the standard does not have.\n * ⛔ Same rule as `except`: the reason is a written literal or it does not compile.\n */\nexport function extraJob<const J extends ExtraJobInput>(\n job: J & { readonly reason: Stated<J['reason']> },\n): ExtraJob {\n if (job.steps.length === 0) {\n throw new Error(`repo-shape: extra job \"${job.id}\" has no steps`);\n }\n return {\n bun: job.bun ?? true,\n id: requireJobId(job.id),\n // ⚠️ A DISPLAY NAME IS NOT A SENTENCE. `build` and `consumer smoke test` are both\n // correct job names; only the REASON has to be prose, because only the reason is\n // there for a reader rather than for the checks list.\n name: requireNonEmpty('name', job.name),\n needs: [...(job.needs ?? [])],\n reason: requireSentence('reason', job.reason),\n steps: [...job.steps],\n ...(job.timeout === undefined ? {} : { timeout: requireMinutes(job.timeout) }),\n };\n}\n\nexport interface RepoShape {\n /** Repository owner — a user or organization login. */\n readonly owner: string;\n /** Repository name, which is also the checkout directory name. */\n readonly repository: string;\n /** Where its jobs run. */\n readonly runner: RepoRunner;\n /**\n * `true` when the repository publishes an npm tarball. It decides `access` in the\n * changeset config and whether `privatePackages` is written at all — the two keys\n * that differ between `homeflare-kit` and every other repository.\n */\n readonly publishes: boolean;\n /**\n * Node major to install before Bun, for a repository whose own gate needs a real\n * `node` on `PATH`. Omit it — twelve of fourteen repositories are Bun-only.\n *\n * ⛔ AN INPUT, NOT AN EXCEPTION, AND THE MEASUREMENT SAYS WHY. The mini's job image\n * carries no node at all (ubuntu-latest always did), so a Bun-only prologue is right\n * for most of the estate and *silently wrong* for two repositories:\n * · homeflare-alerts — tests/alchemy-import.test.ts spawns `node` to prove the\n * modules load the way the Alchemy CLI (`node …/cli.js`) loads them. Without it,\n * three tests fail with `Executable not found in $PATH: \"node\"` (measured on the\n * runner, 2026-09-22).\n * · homeflare-blog — Payload requires Node >= 24.15, so every lane needs it.\n * Both repositories carried the same hand-written `actions/setup-node@v6` block\n * before this existed. Excepting `ci.yml` instead would hand the estate's two most\n * complicated CI files back to hand-editing, which is the opposite of the point.\n *\n * ⚠️ `package-manager-cache: false` IS RENDERED WITH IT. Bun does the installing here;\n * letting setup-node prime an npm cache costs time and caches nothing anyone reads.\n */\n readonly node?: number;\n /** Jobs beyond `check` and `workflows`. Each carries its own stated reason. */\n readonly extraJobs?: readonly ExtraJob[];\n /** Rendered files this repository keeps its own copy of, each with a reason. */\n readonly exceptions?: readonly RepoShapeException[];\n}\n\n/**\n * The validated Node major this shape asks for, or `undefined` for a Bun-only prologue.\n * ★ `RepoShape` is a plain object, so this is where `node:` is checked — at render, not\n * at declaration. A bad value fails the refresh rather than the job.\n */\nexport function nodeMajor(shape: RepoShape): number | undefined {\n return shape.node === undefined ? undefined : requireMajor(shape.node);\n}\n\n/** `runs-on:` for a runner. */\nexport function runsOn(runner: RepoRunner): string {\n return runner === 'mini' ? '[self-hosted, homeflare-mini]' : 'ubuntu-latest';\n}\n\n/** Whether a rendered path is excepted by this shape. */\nexport function isExcepted(shape: RepoShape, file: RenderedPath): boolean {\n return (shape.exceptions ?? []).some((exception) => exception.file === file);\n}\n",
8
- "/**\n * The smallest YAML writer that renders a job's steps, and nothing else.\n *\n * ★ NOT A YAML LIBRARY, DELIBERATELY. A general serializer would take a dependency every\n * consumer of `@homeflare/config` inherits, and it would strip the comments — which in\n * this house are the product. The rendered workflows are written as text with holes;\n * only the step lists, whose shape varies per repository, go through here.\n *\n * ⚠️ Bun can PARSE YAML natively (`Bun.YAML.parse`) but does not stringify it, measured\n * against Bun 1.4.0 on 2026-09-22. The tests parse what this writes with `Bun.YAML.parse`\n * and compare structures, so a malformed emission fails rather than shipping.\n */\nimport type { JobStep } from './shape.ts';\n\n/** Two spaces per level, the house indent and GitHub's own. */\nconst INDENT = ' ';\n\nfunction indent(depth: number): string {\n return INDENT.repeat(depth);\n}\n\n/**\n * ⛔ QUOTE ANYTHING YAML WOULD RE-READ AS SOMETHING ELSE. `09:00` is a sexagesimal number\n * in YAML 1.1 and `on`/`no` are booleans; an unquoted value that looks like either\n * reaches GitHub as the wrong type, and the symptom is a schedule that never fires\n * rather than an error.\n */\nconst PLAIN = /^[A-Za-z0-9_./][A-Za-z0-9_ ./@:+-]*$/;\nconst YAML_KEYWORD =\n /^(y|Y|n|N|on|On|ON|no|No|NO|yes|Yes|YES|true|True|TRUE|false|False|FALSE|null|Null|NULL|~)$/;\n\nfunction scalar(value: string | number): string {\n if (typeof value === 'number') return String(value);\n if (value === '') return \"''\";\n const plain =\n PLAIN.test(value) &&\n !YAML_KEYWORD.test(value) &&\n !value.includes(': ') &&\n !value.includes(' #') &&\n // ⚠️ A LEADING DIGIT PLUS A COLON IS A YAML 1.1 SEXAGESIMAL NUMBER, not a string:\n // `09:00` would reach GitHub as an integer, and a schedule set to an integer\n // simply never fires. `bun run build:web` is safe because it does not start with\n // a digit, which is why this is narrower than \"contains a colon\".\n !/^\\d.*:/.test(value) &&\n value.trimEnd() === value;\n return plain ? value : `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction renderMapping(\n entries: Readonly<Record<string, string | number>>,\n depth: number,\n): string[] {\n return Object.entries(entries).map(([key, value]) => `${indent(depth)}${key}: ${scalar(value)}`);\n}\n\n/**\n * ★ BLOCK SCALAR FOR EVERY MULTI-LINE `run:`. A folded or quoted form would join the\n * lines, and a shell script whose `if` and `then` end up on one line is a syntax error\n * at job time rather than at lint time. `|` keeps them exactly as written.\n */\nfunction renderRun(command: string, depth: number): string[] {\n const lines = command.replace(/\\n+$/, '').split('\\n');\n if (lines.length === 1) return [`${indent(depth)}run: ${scalar(lines[0] ?? '')}`];\n return [`${indent(depth)}run: |`, ...lines.map((line) => `${indent(depth + 1)}${line}`)];\n}\n\n/** One step, as the lines of a `steps:` list item at `depth`. */\nexport function renderStep(step: JobStep, depth: number): string[] {\n const lines: string[] = [];\n const body =\n step.uses === undefined\n ? renderRun(step.run ?? '', depth + 1)\n : [`${indent(depth + 1)}uses: ${step.uses}`];\n\n // ★ `name:` then `if:` then the body, because that is the order a reader scans: what\n // this step is, whether it runs, what it does. All three are ordinary mapping keys\n // to GitHub, so the order is for the person reading the diff, not the parser.\n const head: string[] = [];\n if (step.name !== undefined) head.push(`${indent(depth + 1)}name: ${scalar(step.name)}`);\n if (step.if !== undefined) head.push(`${indent(depth + 1)}if: ${scalar(step.if)}`);\n\n // ⚠️ The first line of the item carries the dash, whichever key it turns out to be —\n // an unnamed, unconditional step still inlines its `run:` or `uses:` after the dash\n // and lets any block body follow at its own indent.\n const [first = '', ...rest] = [...head, ...body];\n lines.push(`${indent(depth)}- ${first.trimStart()}`, ...rest);\n\n if (step.with !== undefined && Object.keys(step.with).length > 0) {\n lines.push(`${indent(depth + 1)}with:`, ...renderMapping(step.with, depth + 2));\n }\n if (step.env !== undefined && Object.keys(step.env).length > 0) {\n lines.push(`${indent(depth + 1)}env:`, ...renderMapping(step.env, depth + 2));\n }\n return lines;\n}\n\n/** A whole `steps:` list, already indented for a job at `depth`. */\nexport function renderSteps(steps: readonly JobStep[], depth: number): string {\n return steps.flatMap((step) => renderStep(step, depth)).join('\\n');\n}\n",
8
+ "/**\n * The smallest YAML writer that renders a job's steps, and nothing else.\n *\n * ★ NOT A YAML LIBRARY, DELIBERATELY. A general serializer would take a dependency every\n * consumer of `@homeflare/config` inherits, and it would strip the comments — which in\n * this house are the product. The rendered workflows are written as text with holes;\n * only the step lists, whose shape varies per repository, go through here.\n *\n * ⚠️ Bun.YAML.stringify EXISTS (`Object.keys(Bun.YAML)` is `[\"parse\", \"stringify\"]`,\n * measured against Bun 1.4.0 on 2026-09-23) but its output does not fit these rules:\n * (a) a multi-line `run:` comes out as a double-quoted string with `\\n` escapes, e.g.\n * `run: \"echo a\\necho b\\n\"`, not a `|` block scalar; (b) `09:00` comes out UNQUOTED,\n * e.g. `cron: 09:00`, the YAML 1.1 sexagesimal trap the `scalar()` comment below guards\n * against; (c) a mapping key is followed by a trailing space, e.g. `\"steps: \\n - ...\"`;\n * and (d) a plain JS object has nowhere to attach a comment, so it cannot carry one. The\n * tests parse what this writes with `Bun.YAML.parse` and compare structures, so a\n * malformed emission fails rather than shipping.\n */\nimport type { JobStep } from './shape.ts';\n\n/** Two spaces per level, the house indent and GitHub's own. */\nconst INDENT = ' ';\n\nfunction indent(depth: number): string {\n return INDENT.repeat(depth);\n}\n\n/**\n * ⛔ QUOTE ANYTHING YAML WOULD RE-READ AS SOMETHING ELSE. `09:00` is a sexagesimal number\n * in YAML 1.1 and `on`/`no` are booleans; an unquoted value that looks like either\n * reaches GitHub as the wrong type, and the symptom is a schedule that never fires\n * rather than an error.\n */\nconst PLAIN = /^[A-Za-z0-9_./][A-Za-z0-9_ ./@:+-]*$/;\nconst YAML_KEYWORD =\n /^(y|Y|n|N|on|On|ON|no|No|NO|yes|Yes|YES|true|True|TRUE|false|False|FALSE|null|Null|NULL|~)$/;\n\nfunction scalar(value: string | number): string {\n if (typeof value === 'number') return String(value);\n if (value === '') return \"''\";\n const plain =\n PLAIN.test(value) &&\n !YAML_KEYWORD.test(value) &&\n !value.includes(': ') &&\n !value.includes(' #') &&\n // ⚠️ A LEADING DIGIT PLUS A COLON IS A YAML 1.1 SEXAGESIMAL NUMBER, not a string:\n // `09:00` would reach GitHub as an integer, and a schedule set to an integer\n // simply never fires. `bun run build:web` is safe because it does not start with\n // a digit, which is why this is narrower than \"contains a colon\".\n !/^\\d.*:/.test(value) &&\n value.trimEnd() === value;\n return plain ? value : `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction renderMapping(\n entries: Readonly<Record<string, string | number>>,\n depth: number,\n): string[] {\n return Object.entries(entries).map(([key, value]) => `${indent(depth)}${key}: ${scalar(value)}`);\n}\n\n/**\n * Trim trailing `\\n` characters the way `command.replace(/\\n+$/, '')` used to, but\n * linear instead of backtracking — the same pattern as `normalizeBaseUrl` in\n * `packages/distilled-netbox/src/credentials.ts`. Exported so a test can compare it\n * against the old regex directly.\n *\n * ⚠️ Linear on purpose: a `/\\n+$/` regex backtracks polynomially on a long run of\n * \"\\n\" that is not at the end (CodeQL js/polynomial-redos), and `command` here is\n * repository-configured step text.\n */\nexport function trimTrailingNewlines(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 10) end--;\n return value.slice(0, end);\n}\n\n/**\n * ★ BLOCK SCALAR FOR EVERY MULTI-LINE `run:`. A folded or quoted form would join the\n * lines, and a shell script whose `if` and `then` end up on one line is a syntax error\n * at job time rather than at lint time. `|` keeps them exactly as written.\n */\nfunction renderRun(command: string, depth: number): string[] {\n const lines = trimTrailingNewlines(command).split('\\n');\n if (lines.length === 1) return [`${indent(depth)}run: ${scalar(lines[0] ?? '')}`];\n return [`${indent(depth)}run: |`, ...lines.map((line) => `${indent(depth + 1)}${line}`)];\n}\n\n/** One step, as the lines of a `steps:` list item at `depth`. */\nexport function renderStep(step: JobStep, depth: number): string[] {\n const lines: string[] = [];\n const body =\n step.uses === undefined\n ? renderRun(step.run ?? '', depth + 1)\n : [`${indent(depth + 1)}uses: ${step.uses}`];\n\n // ★ `name:` then `if:` then the body, because that is the order a reader scans: what\n // this step is, whether it runs, what it does. All three are ordinary mapping keys\n // to GitHub, so the order is for the person reading the diff, not the parser.\n const head: string[] = [];\n if (step.name !== undefined) head.push(`${indent(depth + 1)}name: ${scalar(step.name)}`);\n if (step.if !== undefined) head.push(`${indent(depth + 1)}if: ${scalar(step.if)}`);\n\n // ⚠️ The first line of the item carries the dash, whichever key it turns out to be —\n // an unnamed, unconditional step still inlines its `run:` or `uses:` after the dash\n // and lets any block body follow at its own indent.\n const [first = '', ...rest] = [...head, ...body];\n lines.push(`${indent(depth)}- ${first.trimStart()}`, ...rest);\n\n if (step.with !== undefined && Object.keys(step.with).length > 0) {\n lines.push(`${indent(depth + 1)}with:`, ...renderMapping(step.with, depth + 2));\n }\n if (step.env !== undefined && Object.keys(step.env).length > 0) {\n lines.push(`${indent(depth + 1)}env:`, ...renderMapping(step.env, depth + 2));\n }\n return lines;\n}\n\n/** A whole `steps:` list, already indented for a job at `depth`. */\nexport function renderSteps(steps: readonly JobStep[], depth: number): string {\n return steps.flatMap((step) => renderStep(step, depth)).join('\\n');\n}\n",
9
9
  "/**\n * `.github/workflows/dependabot-automerge.yml`, rendered.\n *\n * ★ THE OTHER HALF OF THE `homeflare` GROUP. Dependabot opens the bump; this arms GitHub's\n * own auto-merge on it; the branch ruleset's required checks (`ci`, `secret scan`)\n * decide whether it ever merges. Nothing here merges anything itself, and a merge\n * deploys nothing — every stack in the estate is deployed by hand.\n *\n * ⛔ `gh pr merge --auto` IS NOT ALWAYS \"ARM\". When the pull request is already CLEAN,\n * UNSTABLE or HAS_HOOKS it merges AT ONCE instead (cli/cli `pkg/cmd/pr/merge/merge.go`,\n * `isImmediatelyMergeable`, read at v2.101.0 — the version the mini's job image ships).\n * UNSTABLE is \"mergeable, with a non-passing status\", which is every pull request on a\n * branch whose rules require no check: before `check` has run, and even after it failed.\n * So the step reads the base branch's active rules first and refuses — a red check, never\n * a merge — when none requires a status check. Measured 2026-09-22: homeflare-builds (its\n * ruleset not deployed yet) and homeflare-desktop have none.\n *\n * ⛔ FIRST-PARTY ONLY: A `run:` STEP AND THE GitHub CLI, NO `uses:` AT ALL. GitHub's own\n * example (\"Automating Dependabot with GitHub Actions\") identifies the update with\n * `dependabot/fetch-metadata`, which the same page marks as \"not certified by GitHub\".\n * The group is identified by the branch Dependabot gives it instead, whose format is\n * dependabot-core's (`branch_namer/dependency_group_strategy.rb`, read at v0.397.0):\n * `<prefix>/<package manager>/<directory>/<group>-<10 hex MD5 digest>`, with the root\n * directory collapsing to nothing.\n */\nimport { HOMEFLARE_GROUP } from './dependabot.ts';\nimport type { RepoShape } from './shape.ts';\nimport { runsOn } from './shape.ts';\nimport { renderSteps } from './yaml.ts';\n\n/**\n * The branch prefix a job-level `if:` can test with `startsWith`. GitHub's expression\n * language has no regex, so this is the cheap filter that keeps every other pull request\n * from taking a runner slot; `GROUP_BRANCH` is the exact test inside the step.\n */\nexport const GROUP_BRANCH_PREFIX: string = `dependabot/bun/${HOMEFLARE_GROUP}-`;\n\n/**\n * ⛔ EXACT, AND FAILS CLOSED. A solo update of `@homeflare/config` is\n * `dependabot/bun/homeflare/config-0.9.0` and a package named `homeflare-x` would be\n * `dependabot/bun/homeflare-x-1.2.3`; neither matches. If Dependabot ever changes its\n * format, the symptom is a bump that waits for a person — never one merged by mistake.\n */\nexport const GROUP_BRANCH: string = `^${GROUP_BRANCH_PREFIX}[0-9a-f]{10}$`;\n\nconst HEADER = `# Arms auto-merge on Dependabot's @homeflare/* group, and on nothing else.\n#\n# 🤖 RENDERED BY @homeflare/config — DO NOT EDIT THIS FILE BY HAND.\n# Its input is this repository's \\`repo-shape.ts\\`; refresh with \\`bun run repo-shape:refresh\\`.\n#\n# ★ HOW A KIT RELEASE ARRIVES: the \\`${HOMEFLARE_GROUP}\\` group in .github/dependabot.yml opens one\n# pull request; this arms GitHub's auto-merge on it; the ruleset's required checks decide.\n# Nothing here merges anything itself, and merging deploys nothing.\n# ⛔ NO THIRD-PARTY ACTION. GitHub's own example uses dependabot/fetch-metadata, which its\n# docs mark \"not certified by GitHub\"; the group is recognised by its branch name instead.\n# ⛔ NO REQUIRED CHECK ON THE BASE BRANCH, NO ARMING: there \\`gh pr merge --auto\\` would merge\n# at once, unchecked. The job fails instead, so the pull request waits for a person.\n# ⚠️ A RENDERER CHANGE STILL NEEDS A PERSON. When a kit release changes what @homeflare/config\n# renders, the bump fails \\`check\\` (the drift test) and never goes green. Run\n# \\`bun run repo-shape:refresh\\` on Dependabot's branch and push. ⛔ This workflow does not do\n# it for you: a push made with GITHUB_TOKEN starts no workflow run, so a refreshed commit\n# would never get the required checks and the pull request would wait forever.\n`;\n\nconst TRIGGER = `name: dependabot auto-merge\n\non:\n pull_request:\n\n# ⛔ NOTHING AT THE TOP; the one job asks for exactly what \\`gh pr merge --auto\\` needs.\npermissions: {}\n\nconcurrency:\n group: automerge-\\${{ github.ref }}\n cancel-in-progress: true\n`;\n\nconst JOB_NOTE = ` # ⛔ BOTH LOGINS, NOT EITHER. \\`user.login\\` is who opened the pull request; \\`github.actor\\` is\n # who started this run. A person pushing to Dependabot's branch changes the actor, so\n # their commit never arms anything; only Dependabot's own rebases do.`;\n\nconst PERMISSIONS_NOTE = ` # ★ WHAT GitHub'S OWN EXAMPLE GRANTS FOR THIS STEP, AND NO MORE. A run Dependabot starts gets\n # a read-only GITHUB_TOKEN unless the workflow raises it (\"Troubleshooting Dependabot on\n # GitHub Actions\" → \"Changing GITHUB_TOKEN permissions\").`;\n\nconst ARM = `set -euo pipefail\nif [[ ! \"$HEAD_REF\" =~ ${GROUP_BRANCH} ]]; then\n echo \"::notice::$HEAD_REF is not the ${HOMEFLARE_GROUP} group's branch; auto-merge not armed\"\n exit 0\nfi\n# ⛔ gh merges a CLEAN or UNSTABLE pull request at once rather than arming it. Only a branch\n# rule that requires a status check keeps it BLOCKED until the checks have passed.\nrequired=$(gh api \"repos/$GITHUB_REPOSITORY/rules/branches/$BASE_REF\" \\\\\n --jq '[.[] | select(.type == \"required_status_checks\")] | length')\nif [ \"$required\" = 0 ]; then\n echo \"::error::$BASE_REF requires no status check, so gh would merge at once; auto-merge not armed\"\n exit 1\nfi\n# ★ --squash: the house repositories allow squash merges only (declareRepoPolicy).\ngh pr merge --auto --squash \"$PR_URL\"`;\n\n/** The whole `dependabot-automerge.yml` for a shape. */\nexport function renderAutomerge(shape: RepoShape): string {\n // ⚠️ GitHub ACTIONS EXPRESSIONS, NOT TEMPLATE LITERALS: the runner interpolates `${{ … }}`\n // at job time, so they must reach the file verbatim (see security.ts for the same note).\n // oxlint-disable-next-line no-template-curly-in-string\n const headRef = '${{ github.head_ref }}';\n // oxlint-disable-next-line no-template-curly-in-string\n const baseRef = '${{ github.base_ref }}';\n // oxlint-disable-next-line no-template-curly-in-string\n const prUrl = '${{ github.event.pull_request.html_url }}';\n // oxlint-disable-next-line no-template-curly-in-string\n const token = '${{ secrets.GITHUB_TOKEN }}';\n const condition = [\n \"github.event.pull_request.user.login == 'dependabot[bot]'\",\n \"github.actor == 'dependabot[bot]'\",\n `startsWith(github.head_ref, '${GROUP_BRANCH_PREFIX}')`,\n ].join(' && ');\n\n return `${HEADER}\n${TRIGGER}\njobs:\n${JOB_NOTE}\n arm:\n name: arm auto-merge\n if: ${condition}\n runs-on: ${runsOn(shape.runner)}\n${PERMISSIONS_NOTE}\n permissions:\n contents: write\n pull-requests: write\n steps:\n${renderSteps(\n [\n {\n env: { BASE_REF: baseRef, GH_TOKEN: token, HEAD_REF: headRef, PR_URL: prUrl },\n name: 'Arm auto-merge on the homeflare group',\n run: ARM,\n },\n ],\n 3,\n)}\n`;\n}\n",
10
10
  "/**\n * `.github/workflows/ci.yml`, rendered.\n *\n * ★ THE COMMENTS ARE PART OF THE RENDER, NOT DECORATION. Thirteen repositories carried\n * thirteen hand-edited copies of the same reasoning; measured 2026-09-22, the header\n * comment alone had four different wordings and the actionlint block had three. Written\n * here once, every repository gets the same explanation, and correcting it is one edit.\n *\n * ⛔ THE AGGREGATE `ci` JOB IS THE ONLY REQUIRED CHECK. Every other job feeds it through\n * `needs`, so adding a job never means editing a branch ruleset. `repoShapeChecks()`\n * returns exactly `['ci', 'secret scan']`, which is what `declareRepoPolicy` requires.\n */\nimport type { ExtraJob, JobStep, RepoShape } from './shape.ts';\nimport { nodeMajor, runsOn } from './shape.ts';\nimport { renderSteps } from './yaml.ts';\n\n/** Bun the whole estate is pinned to. One line, one place. */\nexport const BUN_VERSION = '1.4.0';\n/** actionlint the `workflow lint` job runs, and the mini's job image preloads. */\nexport const ACTIONLINT_VERSION = '1.7.12';\n\nconst CHECKOUT = 'actions/checkout@v7';\nconst SETUP_BUN = 'oven-sh/setup-bun@v2';\nconst SETUP_NODE = 'actions/setup-node@v6';\n\nfunction runnerNote(shape: RepoShape): string {\n if (shape.runner !== 'mini') {\n return \"# ⚠️ RUNS ON GitHub-HOSTED `ubuntu-latest`. Only a public repository should: hosted minutes\\n# are refused for this account's private repos (billing lock, 2026-09-22).\\n\";\n }\n return [\n '# ⚠️ RUNS ON THE MINI (`[self-hosted, homeflare-mini]`): GitHub-hosted runners are refused for',\n '# this account (billing lock, 2026-09-22), so every job here runs in a one-job Linux arm64',\n \"# container started by homeflare-mini's src/ci-runner (docs/ci-runner.md there). Each action\",\n `# below was checked for linux/arm64. The job image preloads bun ${\n BUN_VERSION\n } where setup-bun`,\n '# looks, gh, actionlint, gitleaks, typos and shfmt; no node on PATH.',\n '',\n ].join('\\n');\n}\n\nconst HEADER = `# Pull requests: is this code correct?\n#\n# 🤖 RENDERED BY @homeflare/config — DO NOT EDIT THIS FILE BY HAND.\n# Its input is this repository's \\`repo-shape.ts\\`. Change that, then:\n# bun run repo-shape:refresh\n# A hand edit is reverted by the next refresh and fails \\`bun run check\\` before that.\n# A file this repository must own outright is declared as an \\`except({...})\\` with a\n# reason, which stops the check from comparing it — see @homeflare/config/repo-shape.\n#\n# ★ SPLIT BY CONCERN, NOT ONE BIG JOB — AND NOT ONE JOB PER COMMAND EITHER: lanes that\n# share a prologue are steps inside \\`check\\`, so the install is paid once.\n# ⛔ EVERY ACTION IS FIRST-PARTY OR THE VENDOR'S OWN, PINNED TO A MAJOR TAG.\n`;\n\nconst TRIGGER = `name: ci\n\non:\n # ⛔ NO \\`push: branches: [main]\\`. Every commit reaches main through a pull request whose\n # \\`ci\\` had to be green — the branch ruleset requires it and carries no bypass actors —\n # so a second run on the squash commit recomputed an answer it already had. Measured\n # 2026-09-15..22 across the estate: 613 of 619 main-push runs had a head_sha identical\n # to the merge_commit_sha of an already-green PR. That duplication was ~41% of the Mac\n # mini's entire CI load.\n # ⚠️ WHAT THIS GIVES UP, SAID OUT LOUD: the ~4% of merges whose base DID move between the\n # PR run and the squash no longer get a post-merge re-test. That run gated nothing — it\n # reported after main already had the commit — and the next PR, which branches from the\n # merged main, is what actually catches a semantic conflict.\n pull_request:\n\npermissions:\n contents: read\n\nconcurrency:\n group: ci-\\${{ github.ref }}\n cancel-in-progress: true\n`;\n\nconst CHECK_NOTE = ` # ── One job, one install ────────────────────────────────────────────────────\n # ★ WAS 3 SEPARATE JOBS: lint and format, types, tests. They shared an identical\n # prologue — checkout, setup-bun, \\`bun install --frozen-lockfile\\` — and the install,\n # not the check, was the cost: measured on the mini 2026-09-22, install p50 51s /\n # p90 81s against 1-3s for the command the install existed to enable. 3 jobs meant 3\n # installs and 3 containers to compute one answer, and on a 3-slot pool that was one\n # pull request asking for every slot to answer a single question.\n # ⛔ ONE STEP: \\`bun run check\\`, THE REPOSITORY'S OWN GATE, NOT A COPY OF ITS LANES.\n # The house rule is that CI runs the same command a person runs, and a workflow that\n # re-lists \\`lint\\`, \\`types\\`, \\`test\\` is a second copy of that command which can quietly\n # check LESS than the local gate. Measured 2026-09-22: \\`bun run check\\` in homeflare-kit\n # is \\`lint && types && build && test\\`, and its tests/dist.test.ts SKIPS ITSELF when\n # dist/ is absent — so a workflow running lint/types/test without the build would drop\n # that test silently and still report green. homeflare-alerts' check runs\n # \\`check:types\\` and \\`build:web\\`; homeflare-subnet-calc's delegates to \\`verify\\`. All\n # fourteen repositories have a \\`check\\` script and every one is local-only — no network,\n # no deploy. Checked, not assumed.\n # ★ THE SPLIT OF RESPONSIBILITY: this renderer owns the PLUMBING — triggers, permissions,\n # concurrency, runner, action versions, the aggregate gate — and the repository owns\n # WHAT ITS GATE RUNS, in package.json, where a change to it shows up in that\n # repository's own diff rather than in a workflow nobody reads.\n # ⚠️ THE TRADE, STATED: a red X says \\`check\\` rather than naming the lane. \\`bun run check\\`\n # short-circuits on the first failure and its output names the lane, which is the same\n # signal a person gets locally.`;\n\nconst WORKFLOWS_NOTE = ` # ★ \\`workflow lint\\` STAYS ITS OWN JOB on purpose: it needs no \\`bun install\\` at all\n # (actionlint is preloaded on the mini's job image — measured 5s end to end), so\n # folding it into \\`check\\` would make a YAML-only change pay the install for nothing.`;\n\nconst ACTIONLINT_NOTE = ` # ⛔ THERE IS NO FIRST-PARTY actionlint ACTION, and the npm package by that name is\n # an unrelated wasm port with no \\`bin\\`. The vendor's own documented CI path is\n # \\`download-actionlint.bash\\` — but that script does NOT verify a checksum (read at\n # the v${ACTIONLINT_VERSION} tag, 2026-09-16: \\`curl -L \"$url\" | tar xvz\\`, no sha256sum anywhere).\n # rhysd's releases DO publish a \\`_checksums.txt\\` per version; this verifies against\n # that instead of trusting the tarball on receipt.\n # ⚠️ ARCH FROM THE RUNNER, NOT HARD-CODED: the mini's runners are arm64, and an amd64\n # binary fails with \"exec format error\" (the checksum file lists both).`;\n\nconst INSTALL_ACTIONLINT = `set -euo pipefail\nversion=${ACTIONLINT_VERSION}\nif command -v actionlint >/dev/null 2>&1 &&\n [ \"$(actionlint -version 2>/dev/null | sed -n 1p)\" = \"\\${version}\" ]; then\n cp \"$(command -v actionlint)\" ./actionlint\n exit 0\nfi\ncase \"$(uname -m)\" in\n x86_64) arch=amd64 ;;\n aarch64 | arm64) arch=arm64 ;;\n *) echo \"unsupported architecture $(uname -m)\" >&2; exit 1 ;;\nesac\nfile=\"actionlint_\\${version}_linux_\\${arch}.tar.gz\"\nbase=\"https://github.com/rhysd/actionlint/releases/download/v\\${version}\"\ncurl -sSfLO \"\\${base}/\\${file}\"\ncurl -sSfLO \"\\${base}/actionlint_\\${version}_checksums.txt\"\ngrep \" \\${file}\\\\$\" \"actionlint_\\${version}_checksums.txt\" | sha256sum -c -\ntar xzf \"\\${file}\" actionlint`;\n\nconst AGGREGATE_NOTE = ` # ── The one check a branch rule can require ─────────────────────────────────\n # ★ A single required check that depends on all of them. Without this, adding a job\n # means editing the branch ruleset too, and forgetting to means the new job is\n # advisory without anyone noticing.\n # ⛔ FAILS ON ANYTHING OTHER THAN success, INCLUDING skipped. A job result has exactly\n # four values: success, failure, cancelled, skipped. Checking only the first two lets\n # a skipped job — a bad \\`if:\\` condition, a misconfigured dependency, a runner picking\n # up nothing — report this aggregate green with a required job never having run.`;\n\nconst VERIFY = `if [ \"\\${{ contains(needs.*.result, 'failure') }}\" = \"true\" ] || \\\\\n [ \"\\${{ contains(needs.*.result, 'cancelled') }}\" = \"true\" ] || \\\\\n [ \"\\${{ contains(needs.*.result, 'skipped') }}\" = \"true\" ]; then\n echo \"one or more required jobs did not succeed: \\${{ toJSON(needs.*.result) }}\" >&2\n exit 1\nfi`;\n\nconst NODE_NOTE = ` # ⛔ REAL NODE, NOT BUN'S SHIM, AND ONLY WHERE THE GATE NEEDS IT. The mini's job\n # image has no node on PATH (ubuntu-latest always did), so a repository whose own\n # \\`check\\` spawns \\`node\\` — or whose framework demands a Node runtime — fails with\n # \\`Executable not found in $PATH: \"node\"\\` without this. Declared as \\`node:\\` in\n # repo-shape.ts, so it is one input rather than a hand-edited block per repository.\n # ⚠️ NO PACKAGE-MANAGER CACHE: bun does the installing, so priming npm's cache costs\n # time and caches nothing anything here reads.`;\n\nfunction prologue(shape: RepoShape): string {\n const node = nodeMajor(shape);\n const bun: JobStep[] = [\n { uses: SETUP_BUN, with: { 'bun-version': BUN_VERSION } },\n { run: 'bun install --frozen-lockfile' },\n ];\n if (node === undefined) return renderSteps([{ uses: CHECKOUT }, ...bun], 3);\n return [\n renderSteps([{ uses: CHECKOUT }], 3),\n NODE_NOTE,\n renderSteps(\n [{ uses: SETUP_NODE, with: { 'node-version': node, 'package-manager-cache': 'false' } }],\n 3,\n ),\n renderSteps(bun, 3),\n ].join('\\n');\n}\n\nfunction renderExtraJob(job: ExtraJob, shape: RepoShape, on: string): string {\n const needs =\n (job.needs ?? []).length === 0 ? '' : ` needs: [${(job.needs ?? []).join(', ')}]\\n`;\n // ★ A TIMEOUT IS THE JOB'S, NOT THE SHAPE'S. Only a job that starts something with its\n // own wait needs one, and it is rendered where a reader looks for it.\n const timeout = job.timeout === undefined ? '' : ` timeout-minutes: ${job.timeout}\\n`;\n const steps =\n job.bun === false\n ? renderSteps([{ uses: CHECKOUT }, ...job.steps], 3)\n : [prologue(shape), renderSteps(job.steps, 3)].join('\\n');\n // ★ The stated reason is rendered into the file. A job nobody can explain is a job\n // nobody dares delete, so the explanation travels with it.\n return ` # ★ NOT PART OF THE STANDARD SHAPE — ${job.reason}\n ${job.id}:\n name: ${job.name}\n${needs} runs-on: ${on}\n${timeout} steps:\n${steps}\n`;\n}\n\n/** The whole `ci.yml` for a shape. */\nexport function renderCi(shape: RepoShape): string {\n const on = runsOn(shape.runner);\n const extras = shape.extraJobs ?? [];\n const needs = ['check', 'workflows', ...extras.map((job) => job.id)];\n\n return `${HEADER}${runnerNote(shape)}${TRIGGER}\njobs:\n${CHECK_NOTE}\n check:\n name: check\n runs-on: ${on}\n steps:\n${prologue(shape)}\n${renderSteps([{ run: 'bun run check' }], 3)}\n\n${WORKFLOWS_NOTE}\n workflows:\n name: workflow lint\n runs-on: ${on}\n steps:\n${renderSteps([{ uses: CHECKOUT }], 3)}\n${ACTIONLINT_NOTE}\n${renderSteps([{ name: 'Install actionlint (checksum-verified)', run: INSTALL_ACTIONLINT }], 3)}\n${renderSteps([{ name: 'Lint workflows', run: './actionlint -color' }], 3)}\n\n${extras.map((job) => `${renderExtraJob(job, shape, on)}\\n`).join('')}${AGGREGATE_NOTE}\n ci:\n name: ci\n if: always()\n needs: [${needs.join(', ')}]\n runs-on: ${on}\n steps:\n${renderSteps([{ name: 'Verify every job succeeded', run: VERIFY }], 3)}\n`;\n}\n",
11
11
  "/**\n * The two smaller rendered files: actionlint's config and the changeset config.\n * (Dependabot was the third; it grew a group and a schedule rule, so it is `dependabot.ts`.)\n *\n * ★ EACH ONE WAS MEASURED BEFORE IT WAS RENDERED (2026-09-22, across 13 repositories):\n * · `.changeset/config.json` — byte-identical apart from the repository name in 11 of\n * 13. The three that differed did so by accident: two pinned an older `$schema`\n * (3.1.4 against 4.0.1) and one used `@changesets/cli/changelog` instead of the\n * GitHub changelog everyone else had, which silently drops PR links from releases.\n * · `.github/actionlint.yaml` — 12 of 13 had it, with three wordings of one comment.\n * The one without it is the one repository still on `ubuntu-latest`, which is the\n * only case where it is genuinely not needed. That is an input, not an exception.\n */\nimport type { RepoShape } from './shape.ts';\n\n/**\n * ⚠️ PINNED, NOT `latest`. `unpkg.com/@changesets/config@latest/schema.json` would change\n * what editors validate against without a commit, and the two repositories that drifted\n * here drifted by being pinned to different versions — not by being pinned.\n */\nconst CHANGESET_SCHEMA = '4.0.1';\n\n/**\n * `.changeset/config.json`.\n *\n * ⛔ `privatePackages` IS WRITTEN ONLY FOR A REPOSITORY THAT DOES NOT PUBLISH, AND BOTH\n * OF ITS FIELDS ARE REQUIRED. `@changesets/cli` silently versions nothing when\n * `version` is absent, so a private repository without this key opens a Version\n * Packages PR that changes no version and tags no release — a release pipeline that\n * reports success and ships nothing.\n */\nexport function renderChangesetConfig(shape: RepoShape): string {\n const config = {\n $schema: `https://unpkg.com/@changesets/config@${CHANGESET_SCHEMA}/schema.json`,\n // ★ `@changesets/changelog-github` over the built-in: it writes the pull request and\n // author into CHANGELOG.md, which is the difference between a changelog you can\n // audit and a list of sentences.\n changelog: ['@changesets/changelog-github', { repo: `${shape.owner}/${shape.repository}` }],\n commit: false,\n fixed: [] as string[],\n linked: [] as string[],\n access: shape.publishes ? 'public' : 'restricted',\n baseBranch: 'main',\n updateInternalDependencies: 'patch',\n ignore: [] as string[],\n ...(shape.publishes ? {} : { privatePackages: { version: true, tag: false } }),\n };\n return `${JSON.stringify(config, undefined, 2)}\\n`;\n}\n\n/**\n * `.github/actionlint.yaml` — rendered only for a self-hosted runner.\n *\n * ★ DECLARED, NOT SUPPRESSED. actionlint's `runner-label` rule stays on, so a typo'd\n * label (`homeflare-mnii`) still fails the `workflow lint` job rather than silently\n * queueing a job no runner ever claims. Measured 2026-09-22 with actionlint 1.7.12: an\n * undeclared `homeflare-mini` is an error, not a warning, so this file is the reason\n * twelve repositories are green rather than a nicety.\n */\nexport function renderActionlintConfig(shape: RepoShape): string | undefined {\n if (shape.runner !== 'mini') return undefined;\n return `# actionlint's list of self-hosted runner labels (its \\`runner-label\\` check).\n#\n# 🤖 RENDERED BY @homeflare/config — DO NOT EDIT THIS FILE BY HAND.\n# Refresh with \\`bun run repo-shape:refresh\\`.\n#\n# ★ DECLARED, NOT SUPPRESSED: the rule stays on, so a typo'd label (\\`homeflare-mnii\\`)\n# still fails the \\`workflow lint\\` job rather than queueing a job no runner claims.\n# \\`homeflare-mini\\` is the interim runner on the Mac mini (homeflare-mini's\n# docs/ci-runner.md); setting this repository's shape to \\`runner: 'github'\\` removes\n# this file with the \\`runs-on\\` that needed it.\nself-hosted-runner:\n labels:\n - homeflare-mini\n`;\n}\n",
@@ -14,7 +14,7 @@
14
14
  "/**\n * The drift check: re-render, compare, and say what to run.\n *\n * ★ WHAT MAKES THIS DIFFERENT FROM A LINT RULE. It does not describe good workflows; it\n * asserts that this repository's committed files ARE the rendered ones. A hand edit\n * fails here before it can spread, which is the mechanism — not the convention — that\n * keeps fourteen repositories the same.\n *\n * ⛔ IT REPORTS, IT DOES NOT REPAIR, exactly like `checkProject` in this package. A check\n * that silently rewrote the working tree would erase the evidence of what someone\n * changed, and the one case worth catching — a deliberate edit that should have been an\n * exception — is the case it would erase. `refresh.ts` is the separate, explicit writer.\n *\n * ⚠️ EVERY COMPARISON IS EXACT TEXT, NOT STRUCTURE. `checkProject` compares parsed values\n * because a project may reformat its copy of a preset. Here the file IS generated, so\n * any difference at all — including a reflowed comment — means the file did not come\n * out of the renderer, and reformatting it would defeat the point of generating it.\n * The one exception is line endings, normalised so a CRLF checkout is not \"drift\".\n */\nimport { type RenderedRepo, renderRepoShape } from './render.ts';\nimport type { RenderedPath, RepoShape, RepoShapeException } from './shape.ts';\n\n/** One thing to fix, in the imperative — the same `Problem` shape `./check` reports. */\nexport type Problem = string;\n\n/** The command that makes a difference go away. Printed with every drift problem. */\nexport const REFRESH_COMMAND = 'bun run repo-shape:refresh';\n\nfunction normalize(text: string): string {\n return text.replaceAll('\\r\\n', '\\n');\n}\n\nasync function readIfPresent(path: string): Promise<string | undefined> {\n const file = Bun.file(path);\n return (await file.exists()) ? normalize(await file.text()) : undefined;\n}\n\n/**\n * ⛔ AN EXCEPTION FOR A FILE THE SHAPE DOES NOT RENDER IS ITSELF A PROBLEM. Otherwise a\n * repository that moved from the mini to GitHub-hosted runners would keep a stale\n * `.github/actionlint.yaml` exception forever, and the exception list — the one place\n * that is supposed to hold every deviation — would be lying about one of them.\n */\nfunction staleExceptions(\n rendered: RenderedRepo,\n exceptions: readonly RepoShapeException[],\n): Problem[] {\n return exceptions\n .filter((exception) => rendered.files[exception.file] === undefined)\n .map(\n (exception) =>\n `${exception.file}: excepted (since ${exception.since}) but this shape renders no such file — drop the exception`,\n );\n}\n\n/**\n * ⚠️ A DUPLICATE EXCEPTION HIDES A REASON. Two `except()` entries for one file means one\n * of the two reasons is never read, and the one that loses is arbitrary. Refuse both.\n */\nfunction duplicateExceptions(exceptions: readonly RepoShapeException[]): Problem[] {\n const seen = new Set<RenderedPath>();\n const duplicated = new Set<RenderedPath>();\n for (const exception of exceptions) {\n if (seen.has(exception.file)) duplicated.add(exception.file);\n seen.add(exception.file);\n }\n return [...duplicated].map(\n (file) => `${file}: declared as an exception more than once — keep one, with one reason`,\n );\n}\n\nexport interface DriftReport {\n /** Empty when the committed files are the rendered ones. */\n readonly problems: readonly Problem[];\n /** Paths whose committed text differs from the render, excluding excepted files. */\n readonly drifted: readonly string[];\n /** Paths the repository declared it owns, with the reason it gave. */\n readonly excepted: readonly RepoShapeException[];\n}\n\n/**\n * Compare a repository's committed tooling files against its declared shape.\n *\n * const report = await driftInRepoShape(process.cwd(), shape);\n * expect(report.problems).toEqual([]);\n *\n * A declared exception passes. An undeclared difference fails, and so does a missing file.\n */\nexport async function driftInRepoShape(projectDir: string, shape: RepoShape): Promise<DriftReport> {\n const rendered = renderRepoShape(shape);\n const exceptions = shape.exceptions ?? [];\n const excepted = new Map(exceptions.map((exception) => [exception.file as string, exception]));\n const problems: Problem[] = [\n ...duplicateExceptions(exceptions),\n ...staleExceptions(rendered, exceptions),\n ];\n const drifted: string[] = [];\n\n for (const [path, expected] of Object.entries(rendered.files)) {\n if (excepted.has(path)) continue;\n const actual = await readIfPresent(`${projectDir}/${path}`);\n if (actual === undefined) {\n problems.push(`${path}: missing — run \\`${REFRESH_COMMAND}\\``);\n drifted.push(path);\n continue;\n }\n if (actual !== normalize(expected)) {\n drifted.push(path);\n problems.push(\n `${path}: differs from what @homeflare/config renders for this shape. ` +\n `Run \\`${REFRESH_COMMAND}\\` to take the standard, ` +\n `or declare it with except({ file: '${path}', reason: '…', since: '…' }) in repo-shape.ts.`,\n );\n }\n }\n\n return { drifted, excepted: exceptions, problems };\n}\n\n/**\n * Every rendered path that is NOT compared, with why. For a report, not for a gate.\n * ★ Printed by `refresh --check` so a passing run still names what it did not check.\n * An exception that stops being visible is an exception that stops being reconsidered.\n */\nexport function exceptionSummary(shape: RepoShape): readonly string[] {\n return (shape.exceptions ?? []).map(\n (exception) =>\n `${exception.file}: not checked — ${exception.reason} (since ${exception.since})`,\n );\n}\n",
15
15
  "/**\n * The writer, and the CLI a repository wires to `repo-shape:refresh`.\n *\n * ⛔ SEPARATE FROM THE CHECK, ON PURPOSE. `drift.ts` never writes and this never gates;\n * a tool that did both would repair the working tree during CI and report green on a\n * repository whose committed files are still wrong.\n *\n * ★ IT ONLY WRITES WHAT CHANGED. An unchanged file keeps its mtime, so a refresh in a\n * watch-mode session does not restart anything, and `git status` after a no-op refresh\n * is empty rather than \"14 files touched\".\n *\n * ⚠️ IT NEVER WRITES AN EXCEPTED FILE. Refreshing a file the repository declared it owns\n * would overwrite the deviation the reason was written for — the one destructive thing\n * this could do, and the one it must not.\n */\nimport { REFRESH_COMMAND, driftInRepoShape, exceptionSummary } from './drift.ts';\nimport { renderRepoShape } from './render.ts';\nimport { type RepoShape, isExcepted } from './shape.ts';\nimport type { RenderedPath } from './shape.ts';\n\nexport interface RefreshResult {\n /** Paths written because their content changed or they were absent. */\n readonly written: readonly string[];\n /** Paths already correct. */\n readonly unchanged: readonly string[];\n /** Paths skipped because the shape declares an exception for them. */\n readonly skipped: readonly string[];\n}\n\n/** Write a repository's rendered files into `projectDir`. */\nexport async function refreshRepoShape(\n projectDir: string,\n shape: RepoShape,\n): Promise<RefreshResult> {\n const rendered = renderRepoShape(shape);\n const written: string[] = [];\n const unchanged: string[] = [];\n const skipped: string[] = [];\n\n for (const [path, contents] of Object.entries(rendered.files)) {\n if (isExcepted(shape, path as RenderedPath)) {\n skipped.push(path);\n continue;\n }\n const target = `${projectDir}/${path}`;\n const file = Bun.file(target);\n if ((await file.exists()) && (await file.text()) === contents) {\n unchanged.push(path);\n continue;\n }\n await Bun.write(target, contents);\n written.push(path);\n }\n\n return { skipped, unchanged, written };\n}\n\n/**\n * ★ `Bun.write(Bun.stdout, …)` RATHER THAN `console.log`. Two reasons, and the lint rule\n * is the lesser: a CLI's output is a stream it owns, and writing to it explicitly is\n * what lets `repoShapeCli` be called in a test and in a `repo-shape.ts` without either\n * one inheriting a global. `console` in `src/` is also off-limits in the house preset —\n * allowed under `tests/**` and `scripts/**`, and this is neither.\n */\nasync function say(\n stream: typeof Bun.stdout | typeof Bun.stderr,\n lines: readonly string[],\n): Promise<void> {\n if (lines.length === 0) return;\n await Bun.write(stream, `${lines.join('\\n')}\\n`);\n}\n\n/**\n * The `repo-shape:refresh` entry point. `--check` reports drift and exits non-zero\n * instead of writing, which is what a repository puts in CI when it does not want the\n * check inside `bun test`.\n */\nexport async function repoShapeCli(\n projectDir: string,\n shape: RepoShape,\n argv: readonly string[],\n): Promise<number> {\n if (argv.includes('--check')) {\n const report = await driftInRepoShape(projectDir, shape);\n // ★ The exceptions are printed on a PASSING run too. An exception that stops being\n // visible is an exception that stops being reconsidered.\n await say(\n Bun.stdout,\n exceptionSummary(shape).map((note) => `· ${note}`),\n );\n if (report.problems.length === 0) {\n const count = report.excepted.length;\n await say(Bun.stdout, [\n `repo shape: in step with @homeflare/config (${count} declared exception(s))`,\n ]);\n return 0;\n }\n await say(Bun.stderr, [\n ...report.problems.map((problem) => `✗ ${problem}`),\n '',\n `Run \\`${REFRESH_COMMAND}\\`, or declare the deviation with a reason.`,\n ]);\n return 1;\n }\n\n const result = await refreshRepoShape(projectDir, shape);\n await say(Bun.stdout, [\n ...result.written.map((path) => `wrote ${path}`),\n ...result.unchanged.map((path) => `ok ${path}`),\n ...result.skipped.map((path) => `excepted ${path}`),\n ]);\n return 0;\n}\n"
16
16
  ],
17
- "mappings": "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",
18
- "debugId": "2943BDDA9944E35364756E2164756E21",
17
+ "mappings": "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",
18
+ "debugId": "C9A5F54A65373F0A64756E2164756E21",
19
19
  "names": []
20
20
  }