Skip to content

Commit cdb09af

Browse files
dmealingclaude
andcommitted
docs(recipes): pre-commit meta verify (local-dev counterpart to the action)
Drops the dead link in the meta-verify-action README and closes the local-dev side of the drift loop: same command, same exit code, same report, just running on the contributor's laptop before push instead of on GHA after. Three hook-manager recipes (husky / lefthook / plain `.git/hooks`), plus the standard fix-path (`meta gen` + re-stage), perf notes, and escape hatches. CI gate stays the source of truth — pre-commit is just the faster first line of defense. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0158ef2 commit cdb09af

1 file changed

Lines changed: 174 additions & 0 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Pre-commit `meta verify`
2+
3+
Run `meta verify` locally before each commit so metadata-vs-code drift is caught
4+
on your laptop in seconds, not on CI in minutes. Pairs with
5+
[`metaobjectsdev/meta-verify-action`](https://github.com/metaobjectsdev/meta-verify-action)
6+
on the CI side — same command, same exit codes, same drift report.
7+
8+
> **Why bother if CI already catches it?** Pre-commit catches drift before the
9+
> push, which means: no failed-CI noise on PRs, no `fix typo`-style follow-up
10+
> commits to undo bad codegen, and a ~30s feedback loop instead of a ~2-minute
11+
> CI round-trip. The CI gate is still the source of truth — pre-commit is just
12+
> a faster first line of defense.
13+
14+
## TL;DR
15+
16+
```bash
17+
# Whatever hook manager you use, the command is:
18+
npx --no-install meta verify
19+
20+
# Or with a custom templates directory (when templates don't live under ./prompts):
21+
npx --no-install meta verify --prompts data/templates
22+
```
23+
24+
Exit 0 = clean. Non-zero = drift. The drift report goes to stdout — same shape
25+
as the CI action's PR comment.
26+
27+
## Choose your hook manager
28+
29+
The MetaObjects framework is hook-manager-agnostic. Three common setups:
30+
31+
| Manager | Best for | Setup time |
32+
|---|---|---|
33+
| [husky](#husky) | JS/TS projects already using Node tooling | 2 min |
34+
| [lefthook](#lefthook) | Polyglot repos, faster than husky, parallel hooks | 5 min |
35+
| [plain `.git/hooks`](#plain-shell-hook) | Zero dependencies, no install step | 1 min (per dev) |
36+
37+
### husky
38+
39+
Most common choice for a TS-only project. Requires the `husky` dev dependency
40+
and a one-time `prepare` script.
41+
42+
```bash
43+
npm install --save-dev husky
44+
npx husky init
45+
```
46+
47+
Then add to `.husky/pre-commit`:
48+
49+
```bash
50+
#!/usr/bin/env sh
51+
. "$(dirname -- "$0")/_/husky.sh"
52+
53+
npx --no-install meta verify
54+
```
55+
56+
Make it executable: `chmod +x .husky/pre-commit`. Commit the file. Every
57+
contributor who runs `npm install` afterwards gets the hook automatically.
58+
59+
### lefthook
60+
61+
Faster than husky (Go binary, no Node startup) and supports parallel hooks.
62+
Worth the extra config in a busy repo.
63+
64+
```bash
65+
npm install --save-dev lefthook
66+
npx lefthook install
67+
```
68+
69+
`lefthook.yml`:
70+
71+
```yaml
72+
pre-commit:
73+
parallel: true
74+
commands:
75+
meta-verify:
76+
run: npx --no-install meta verify
77+
```
78+
79+
For a project with a non-default templates directory:
80+
81+
```yaml
82+
pre-commit:
83+
parallel: true
84+
commands:
85+
meta-verify:
86+
run: npx --no-install meta verify --prompts data/templates
87+
```
88+
89+
### Plain shell hook
90+
91+
Zero dependencies. Each contributor installs once after cloning. Not portable
92+
across machines via the repo — but useful as a fallback or for a project that
93+
doesn't want a hook-manager dep.
94+
95+
Create `.git/hooks/pre-commit`:
96+
97+
```bash
98+
#!/usr/bin/env bash
99+
set -e
100+
exec npx --no-install meta verify
101+
```
102+
103+
Make it executable: `chmod +x .git/hooks/pre-commit`. Done.
104+
105+
To share across the team without a hook-manager, commit the script to
106+
`.githooks/pre-commit` and have contributors run once:
107+
108+
```bash
109+
git config core.hooksPath .githooks
110+
```
111+
112+
## What the hook catches
113+
114+
Same contract as `meta verify` everywhere — metadata declarations vs.
115+
116+
- **Generated code** in your `src/**/generated/` output. Edit a field name in
117+
the YAML, forget to `meta gen`, the hook fails.
118+
- **Live database schema** (if `DATABASE_URL` is set). Add a column to the
119+
YAML, forget to write the migration, the hook fails.
120+
- **Prompt templates** (`template.prompt`) vs. the `@payloadRef`-bound
121+
`object.value`. Reference `{{undefinedField}}`, the hook fails.
122+
- **Output parsers** (`template.output`) vs. their target payload shapes.
123+
124+
## Fixing drift when the hook fires
125+
126+
```
127+
$ git commit -m "feat: add a field to FooPayload"
128+
129+
ERR_VAR_NOT_ON_PAYLOAD templates/foo-user.mustache:8
130+
{{#items.length}} → `items.length` not on FooPayload field tree
131+
```
132+
133+
Fix path:
134+
135+
1. Run your project's regen command (`npm run gen:db`, `meta gen`, etc.)
136+
2. Stage the regenerated files (`git add src/**/generated/`)
137+
3. Re-run the commit. If the regen produced clean output, the hook passes.
138+
139+
If the failure is a template referencing a field that genuinely doesn't exist
140+
on the payload — that's a real bug. Fix the template (or add the field to the
141+
payload's metadata YAML), regen, re-commit.
142+
143+
## Performance
144+
145+
`meta verify` reads metadata + generated source + (optional) DB schema. On a
146+
typical project (≤50 entities, ≤30 templates) it runs in well under a second.
147+
It will not slow down `git commit` perceptibly.
148+
149+
If you do find it slow, two knobs:
150+
151+
- **Skip the DB check** — leave `DATABASE_URL` unset locally. The hook still
152+
catches code/template drift, which is 95% of what bites you.
153+
- **Scope to staged paths** — `meta verify` is whole-project by design (drift
154+
in one file can be caused by a change in another), but if you have a very
155+
large repo you can pre-filter via `git diff --cached --name-only` and skip
156+
the hook entirely when no metadata or generated file is staged. This is an
157+
escape hatch — most projects won't need it.
158+
159+
## Skipping the hook
160+
161+
Standard git escape hatches work:
162+
163+
```bash
164+
git commit --no-verify -m "wip: temporarily skip the gate"
165+
```
166+
167+
Use sparingly. If you find yourself skipping it routinely, the hook is wrong
168+
— file an issue.
169+
170+
## See also
171+
172+
- [`metaobjectsdev/meta-verify-action`](https://github.com/metaobjectsdev/meta-verify-action) — the CI counterpart
173+
- [`@metaobjectsdev/cli`](https://www.npmjs.com/package/@metaobjectsdev/cli) — the underlying `meta verify` command
174+
- [Drift contract feature doc](../features/) — what counts as drift and why

0 commit comments

Comments
 (0)