# 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