From junjak-ai-harness
Regenerate and re-verify a code-generated API client after any backend/server contract change, BEFORE writing or verifying client code against it. Use whenever a task changes a backend DB schema, request/response DTO, enum, or endpoint shape in a project where the frontend consumes a generated client/types (OpenAPI, GraphQL codegen, gRPC, tRPC, etc.). Trigger this even if the user only says 'I changed the API' or 'update the frontend to match' — the regenerate→typecheck gate is what prevents silent type/runtime desync. Run it at the BE→FE handoff in team workflows, before verification-loop Phase 2, and any time someone reports 'I changed the backend but the frontend doesn't match.'
How this skill is triggered — by the user, by Claude, or both
Slash command
/junjak-ai-harness:contract-syncThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Keep a generated API client in lockstep with the backend contract it was generated from. The failure this prevents: a backend changes a field, an enum, or a nullability, but the frontend keeps compiling against **stale generated types** — so the mismatch surfaces as a runtime error in production instead of a type error at build time.
Keep a generated API client in lockstep with the backend contract it was generated from. The failure this prevents: a backend changes a field, an enum, or a nullability, but the frontend keeps compiling against stale generated types — so the mismatch surfaces as a runtime error in production instead of a type error at build time.
The fix is a strict, ordered gate: make the spec current → regenerate the client → isolate the real change → type-check against the new types → cross-check consumption sites. Skipping any step reintroduces drift.
Applies when ALL hold:
api-layer.md → "Generated Code"). Generators include openapi-generator, orval, swagger-typescript-api, GraphQL Code Generator, tRPC, protobuf/gRPC, Prisma client, etc.Does NOT apply (skip, state why):
team-architect-fe/team-architect-be cross-review, not by regeneration.From .claude/project-profile/api-layer.md collect: the regen command, the spec source (a live server endpoint, e.g. /v3/api-docs, or a committed schema file), the list of hand-maintained files inside the generated tree (overrides the generator must not clobber), and any post-regen fixups. If api-layer.md does not record these, that is itself a gap — record them now (see project-analyzer api-layer.md template) so the next sync is deterministic.
The generator reads a spec; if the spec is stale, you regenerate stale types. So first ensure the spec reflects the change:
Why first: regenerating against a stale spec produces a client that type-checks green yet is wrong — the most expensive failure mode, because nothing flags it.
Run the regen command from the profile (translate package manager to the project's: Bun → pnpm/npm per lockfile). Do not hand-edit generated files to "match" the backend — the backend is the single source of truth; the generator is how the client learns it.
A regen can rewrite many files with formatting noise and can strip hand-maintained files that live inside the generated tree. Separate signal from noise:
Run the project's authoritative type-check (from project-profile stack.md → "Build & Verify", not a convenience alias — a typecheck script can be vacuous; see verification-loop §"Vacuity guard"). Judge results as net-new vs the recorded baseline, not absolute zero (see verification-loop §"Baseline & Net-New").
New type errors here are the gate working: they are the exact places the client consumed the old contract. Fix them by updating the consumption, or — if a type error reveals the backend changed something it should not have — fix it at the source. Never silence with as any/@ts-ignore; a contract type error often flags a real runtime mismatch.
A green type-check proves the new types are internally consistent; it does not prove the runtime shape matches. For each endpoint touched, verify at the actual call site:
resp.data.items[].field, not a guessed path),Confirming a symbol merely exists is insufficient — verify the shape end to end.
# Contract Sync Report
**Spec source**: [endpoint or file] **Regen command**: [command]
**Spec made current via**: [migration applied / server restarted / spec re-exported / N/A]
## Real changes (after churn isolation)
| Generated file | Change | Notes |
|----------------|--------|-------|
| ... | fields added/removed/retyped | ... |
## Overrides restored
- [file]: [restored from HEAD / hand-merged because backend changed it] — or "None"
## Type-check (authoritative command)
- Command: [the real command, e.g. vue-tsc -p .nuxt/tsconfig.app.json]
- Net-new errors vs baseline: [N] (baseline: [B])
- Resolved by: [updated consumption sites / fixed at backend source]
## Consumption sites verified
- [endpoint]: shape OK (names/nullability/nested/enum) — or [mismatch + fix]
## Gate: GREEN (client matches contract) / BLOCKED ([what is still mismatched])
The backend owns types/models/enums; the client consumes generated types and only extends them (interface-extends / Omit) for UI-only fields. Never hand-redefine a domain type to dodge a type error, and never edit generated output to match — fix the contract at the source and regenerate. (CLAUDE.md §"Cross-Boundary Contracts".)
npx claudepluginhub junjak/ai-harness --plugin junjak-ai-harnessCreates bite-sized, testable implementation plans from specs or requirements, with file structure and task decomposition. Activates before coding multi-step tasks.