Files
agentic-dev-template/turbo/generators/verify-doc-shas.test.ts
Danijel Martinek b1e2e8a788 feat(tooling): pre-ship approved library traces in optional core generator templates
Each of the five optional core package generators (events, realtime, audit,
trpc, ui) now copies pre-written decision: approved trace files into
docs/library-decisions/ at scaffold time, covering every direct runtime
dependency of that core package.

This prevents a pre-commit gate failure the first time a developer runs
pnpm turbo gen core-package <name> — the generator is the policy-compliant
path, so the traces land by construction.

- Added docs/library-decisions/*.md.hbs trace files under each of the five
  core-package template directories (15 files total)
- Updated generator config to emit traces into workspace docs/library-decisions/
  via a second emitTemplateTree call per core package
- Updated all five __snapshots__/core-package/*.snapshot.json to include the
  new trace file entries
- Added verify-doc-shas.test.ts to pin SHA256 hashes of all 15 trace templates
  so snapshot and file content cannot drift independently

ADR refs: events→ADR-015, realtime→ADR-016, audit→ADR-018;
trpc and ui cite closest ADR or null where no specific ADR exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 09:34:17 +00:00

124 lines
4.0 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { join } from "node:path";
// Resolve repo root relative to this file (turbo/generators/)
const repoRoot = join(import.meta.dirname, "..", "..");
function sha256(filePath: string): string {
const content = readFileSync(filePath, "utf8");
const normalized = content.replace(/\r\n/g, "\n").replace(/\n*$/, "\n");
return createHash("sha256").update(normalized).digest("hex");
}
const tplBase = join(
repoRoot,
"turbo",
"generators",
"templates",
"core-package",
);
describe("doc template SHA256 checksums", () => {
const cases: Array<{ label: string; rel: string; expected: string }> = [
{
label: "events/zod",
rel: "events/docs/library-decisions/2026-05-14-zod.md.hbs",
expected:
"6b159ae8890a51a0ff1cc94a8cb1ee70bc1343a87fc2e6e4d0b197a6c5801ff7",
},
{
label: "realtime/socket.io",
rel: "realtime/docs/library-decisions/2026-05-14-socket.io.md.hbs",
expected:
"7fd94c82037bdb92a90329355123950132050ca6b7b828292359438f8c9a562f",
},
{
label: "realtime/zod",
rel: "realtime/docs/library-decisions/2026-05-14-zod.md.hbs",
expected:
"412f92181566e983259982cefc0c7c3b632f289d566971badc855f7a5a03f4a5",
},
{
label: "audit/@trpc/server",
rel: "audit/docs/library-decisions/2026-05-14-@trpc/server.md.hbs",
expected:
"40217b9dd1938083adaf3ccf5faa586447b82c5df60c30f662809f3cd668e4d6",
},
{
label: "audit/zod",
rel: "audit/docs/library-decisions/2026-05-14-zod.md.hbs",
expected:
"8f18b4c90031dd453c278f549b8d3dbc20083aacef2e6a422233a893afa29bd9",
},
{
label: "trpc/@tanstack/react-query",
rel: "trpc/docs/library-decisions/2026-05-14-@tanstack/react-query.md.hbs",
expected:
"9e3fe29d5e6f9a9b81fa70a01f966f485e8a7c1a841a1654e117e3171e07aa6c",
},
{
label: "trpc/@trpc/client",
rel: "trpc/docs/library-decisions/2026-05-14-@trpc/client.md.hbs",
expected:
"0c871657850954ee7fb0141070c36f8aa0c79932c510773d292aaf8e65ceb871",
},
{
label: "trpc/@trpc/react-query",
rel: "trpc/docs/library-decisions/2026-05-14-@trpc/react-query.md.hbs",
expected:
"fc02764ca7727d6353ccdc7230f90c5054f6976304ef6c81d7864a917815f002",
},
{
label: "trpc/@trpc/server",
rel: "trpc/docs/library-decisions/2026-05-14-@trpc/server.md.hbs",
expected:
"094ae3505561e84090002a8646967a0ae9dcf4ce121bbbc240da06af2cc078c8",
},
{
label: "trpc/@trpc/tanstack-react-query",
rel: "trpc/docs/library-decisions/2026-05-14-@trpc/tanstack-react-query.md.hbs",
expected:
"f139e506a7b09bd0154818cb9616c7a80295d191a89b7fbd7e3fd2eb7912e8f2",
},
{
label: "trpc/react",
rel: "trpc/docs/library-decisions/2026-05-14-react.md.hbs",
expected:
"dbd8727184632f403404a34f7d26be572461bbbf8923cbbf747438ae4feb1529",
},
{
label: "trpc/superjson",
rel: "trpc/docs/library-decisions/2026-05-14-superjson.md.hbs",
expected:
"b6404b77aa0e222fd6100a7b29e2572f75103a01c7d905d112d90a31e8443eed",
},
{
label: "ui/clsx",
rel: "ui/docs/library-decisions/2026-05-14-clsx.md.hbs",
expected:
"3507768e0ee216dac6983b35b1ac7a6326a85f35a659863d266a340655602429",
},
{
label: "ui/react",
rel: "ui/docs/library-decisions/2026-05-14-react.md.hbs",
expected:
"9d6178b1ec0b0ddba60b83ef8c5d65791f6890092e18d1813f7a2db1e8bb1e94",
},
{
label: "ui/tailwind-merge",
rel: "ui/docs/library-decisions/2026-05-14-tailwind-merge.md.hbs",
expected:
"e8e2474dfc62f95f0671c659c04055371dec7dc9a2f2d26fc318eccaa7d59504",
},
];
for (const { label, rel, expected } of cases) {
it(`${label} matches snapshot`, () => {
const actual = sha256(join(tplBase, rel));
expect(actual).toBe(expected);
});
}
});