M141-2 codegen — step 1 of 4

Draft PR. Ships the codegen scaffolding (spec format + emitter + re-export wrappers + drift gate). Per-event payloads, PyPI parity, and parity-gate CI are deferred to steps 2-4 within #1864.

Pipeline

spec/hook-events.spec.yml
(source of truth)
scripts/codegen.mjs
(parse + emit)
events.generated.ts
schemas.generated.ts
(committed)
events.ts
schemas.ts
(stable re-export)

What ships in this PR (step 1)

spec/hook-events.spec.yml — 19 events authored by hand, envelope-only (matches M141-1 surface). Future steps fill in per-event payload schemas in this file.
packages/hook-contract/scripts/codegen.mjs — node-only, zero deps. Parses the spec, emits events.generated.ts and schemas.generated.ts. Has --check mode for the drift gate.
events.ts / schemas.ts rewritten as one-line re-exports. Public API at @orchestkit/hook-contract/events stays stable across codegen evolutions.
npm scripts: codegen, codegen:check, and a prebuild hook so any build attempt with stale generated files fails fast.

What's deferred (steps 2-4 of M141-2)

Step 2: PyPI sibling package orchestkit-hook-contract — same codegen, Pydantic models, JSON Schema mirrors.
Step 3: Per-event payload field schemas in the spec. Replaces payload: unknown with real types per event.
Step 4: Cross-language parity gate CI — fails if any event's npm and PyPI emit diverge field-by-field.

Verified locally

$ npm run codegen:check       → OK: events.generated.ts + schemas.generated.ts match spec
$ npm run build               → ESM + CJS dual emit (prebuild runs codegen:check first)
$ npm test                    → 15/15 smoke tests pass
$ node --input-type=module ─e 'import { HOOK_EVENT_NAMES } from "./dist/esm/index.js"; console.log(HOOK_EVENT_NAMES.length)'
  → 19
$ node -e 'console.log(require("./dist/cjs/index.js").HOOK_EVENT_NAMES.length)'
  → 19

Drift gate semantics

If someone edits events.generated.ts directly without updating the spec, the next build fails. The --check mode re-emits in memory and compares against the committed file.

$ vim packages/hook-contract/src/events.generated.ts   # add 'FakeEvent' to the list
$ npm run codegen:check
[codegen --check] FAILED: generated files drift from spec. Run `npm run codegen` and commit.
exit 1