@orchestkit/hook-contract (npm) and orchestkit-hook-contract (PyPI) emit semantically identical contracts. Closes the loop on M141-2 + M141-3.
| Check | What fails it |
|---|---|
| Event name set equality | One side has an event the other doesn't |
Per-event payload.required field names | Set difference between sides |
Per-event payload.properties field names | Set difference between sides |
| Per-event field JSON Schema type category | e.g. npm says string while PyPI says object |
┌───────────────────────────────────┐
│ spec/hook-events.spec.yml │
└────────┬────────────────┬─────────┘
│ │
(codegen.mjs) ✓ (codegen-py.py) ✓
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ npm emit │ │ PyPI emit │
└────┬───────────┘ └──┬─────────────┘
│ │
│ per-side │ per-side
│ drift gate │ drift gate
│ (--check) ✓ │ (--check) ✓
│ │
└──────┬──────────┘
▼
┌──────────────────────────┐
│ M141-6 PARITY GATE │
│ (this PR) │
│ │
│ "are npm and PyPI emits │
│ semantically equal?" │
└──────────────────────────┘
A subtle bug in either codegen could let both --check gates pass while npm and PyPI diverge (e.g. one emits a field as type: string and the other as type: object). The cross-language gate catches that case mechanically.
scripts/parity-check.py — diff script. Extracts HOOK_EVENT_SCHEMAS from both packages, runs set/type comparison, exits non-zero with a human-readable diff on divergence..github/workflows/contract-parity.yml — installs both packages, runs each per-side drift gate, then the cross-language gate. Path-filtered on spec/, both package dirs, the script, the workflow itself.package.json — adds npm run test:parity shortcut for local invocation.$ npm run --workspace=@orchestkit/hook-contract build $ cd packages/hook-contract-py && .venv/bin/pip install -e . pyyaml $ python3 scripts/parity-check.py [parity-check] OK: npm + PyPI emit identical contract (19 events, 13 with typed payloads). exit 0 ✓
[parity-check] FAILED: npm and PyPI hook-contract emit diverge.
PreToolUse:
required only in npm: ['session_id']
field 'tool_input' type mismatch: npm=object vs PyPI=string
Fix: identify which codegen drifted from the other, regenerate, and
commit. Both should emit identical schemas from spec/hook-events.spec.yml.