Run meta verify locally before each commit so metadata-vs-code drift is caught
on your laptop in seconds, not on CI in minutes. Pairs with
metaobjectsdev/meta-verify-action
on the CI side — same command, same exit codes, same drift report.
Why bother if CI already catches it? Pre-commit catches drift before the push, which means: no failed-CI noise on PRs, no
fix typo-style follow-up commits to undo bad codegen, and a ~30s feedback loop instead of a ~2-minute CI round-trip. The CI gate is still the source of truth — pre-commit is just a faster first line of defense.
# Whatever hook manager you use, the command is:
npx --no-install meta verify
# Or with a custom templates directory (when templates don't live under ./prompts):
npx --no-install meta verify --prompts data/templatesExit 0 = clean. Non-zero = drift. The drift report goes to stdout — same shape as the CI action's PR comment.
meta verify also prints an advisory anti-pattern pass (hand-rolled
aggregates / money-as-float / CHECK (... IN (...)) enums, with the construct
that models them). It is warnings only — it never changes the exit code, so it
won't block a commit. Silence it with --no-antipatterns or META_NO_ANTIPATTERNS=1.
The MetaObjects framework is hook-manager-agnostic. Three common setups:
| Manager | Best for | Setup time |
|---|---|---|
| husky | JS/TS projects already using Node tooling | 2 min |
| lefthook | Polyglot repos, faster than husky, parallel hooks | 5 min |
plain .git/hooks |
Zero dependencies, no install step | 1 min (per dev) |
Most common choice for a TS-only project. Requires the husky dev dependency
and a one-time prepare script.
npm install --save-dev husky
npx husky initThen add to .husky/pre-commit:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install meta verifyMake it executable: chmod +x .husky/pre-commit. Commit the file. Every
contributor who runs npm install afterwards gets the hook automatically.
Faster than husky (Go binary, no Node startup) and supports parallel hooks. Worth the extra config in a busy repo.
npm install --save-dev lefthook
npx lefthook installlefthook.yml:
pre-commit:
parallel: true
commands:
meta-verify:
run: npx --no-install meta verifyFor a project with a non-default templates directory:
pre-commit:
parallel: true
commands:
meta-verify:
run: npx --no-install meta verify --prompts data/templatesZero dependencies. Each contributor installs once after cloning. Not portable across machines via the repo — but useful as a fallback or for a project that doesn't want a hook-manager dep.
Create .git/hooks/pre-commit:
#!/usr/bin/env bash
set -e
exec npx --no-install meta verifyMake it executable: chmod +x .git/hooks/pre-commit. Done.
To share across the team without a hook-manager, commit the script to
.githooks/pre-commit and have contributors run once:
git config core.hooksPath .githooksSame contract as meta verify everywhere — metadata declarations vs.
- Generated code in your
src/**/generated/output. Edit a field name in the YAML, forget tometa gen, the hook fails. - Live database schema (if
DATABASE_URLis set). Add a column to the YAML, forget to write the migration, the hook fails. - Prompt templates (
template.prompt) vs. the@payloadRef-boundobject.value. Reference{{undefinedField}}, the hook fails. - Output parsers (
template.output) vs. their target payload shapes.
$ git commit -m "feat: add a field to FooPayload"
ERR_VAR_NOT_ON_PAYLOAD templates/foo-user.mustache:8
{{#items.length}} → `items.length` not on FooPayload field tree
Fix path:
- Run your project's regen command (
npm run gen:db,meta gen, etc.) - Stage the regenerated files (
git add src/**/generated/) - Re-run the commit. If the regen produced clean output, the hook passes.
If the failure is a template referencing a field that genuinely doesn't exist on the payload — that's a real bug. Fix the template (or add the field to the payload's metadata YAML), regen, re-commit.
meta verify reads metadata + generated source + (optional) DB schema. On a
typical project (≤50 entities, ≤30 templates) it runs in well under a second.
It will not slow down git commit perceptibly.
If you do find it slow, two knobs:
- Skip the DB check — leave
DATABASE_URLunset locally. The hook still catches code/template drift, which is 95% of what bites you. - Scope to staged paths —
meta verifyis whole-project by design (drift in one file can be caused by a change in another), but if you have a very large repo you can pre-filter viagit diff --cached --name-onlyand skip the hook entirely when no metadata or generated file is staged. This is an escape hatch — most projects won't need it.
Standard git escape hatches work:
git commit --no-verify -m "wip: temporarily skip the gate"Use sparingly. If you find yourself skipping it routinely, the hook is wrong — file an issue.
metaobjectsdev/meta-verify-action— the CI counterpart@metaobjectsdev/cli— the underlyingmeta verifycommand- Drift contract feature doc — what counts as drift and why