From harness-engineering
Protocol Buffers harness configuration guidance. Covers buf lint, buf format, buf breaking (wire compatibility detection), protolint (fallback), field number conventions, reserved tags discipline, and Connect RPC/gRPC integration. Use when adding a new .proto service, troubleshooting buf hook failures, designing a breaking-change-safe schema evolution, or setting up buf.yaml. THIS IS THE GAP that most dev playbooks don't fill.
npx claudepluginhub toru-oizumi/claude-harness-engineering --plugin harness-engineeringThis skill uses the workspace's default tool permissions.
The Proto side of the harness — and the main reason this plugin exists. Proto schema breaks are the most expensive kind of break because they invalidate clients silently. The harness catches them at edit time.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
The Proto side of the harness — and the main reason this plugin exists. Proto schema breaks are the most expensive kind of break because they invalidate clients silently. The harness catches them at edit time.
Unlike Go and TypeScript where a bug is usually a bug in your running code, a Proto break is a bug in everyone else's running code. Once a field number is reused with a different type, old clients reading new messages get garbage data. Once a field is renamed in a JSON/grpc-gateway binding, requests silently drop fields.
A harness that catches buf breaking violations at Write/Edit time is the only reliable defense.
| Role | Tool | Notes |
|---|---|---|
| Format | buf format | Canonical formatter, auto-fix |
| Lint | buf lint | Style + best practices, categories: DEFAULT, COMMENTS, UNARY_RPC, FILE_LAYOUT |
| Breaking-change detection | buf breaking | WIRE / PACKAGE / FILE / WIRE_JSON categories |
| Fallback lint | protolint | Used when buf not available |
| Codegen | buf generate | For Connect RPC, gRPC, grpc-gateway |
buf.yaml# Protected by pre-config-protect hook
version: v2
modules:
- path: proto
lint:
use:
- DEFAULT
- COMMENTS
- FILE_LAYOUT
except:
- PACKAGE_VERSION_SUFFIX # only if you don't want v1/v2 suffix
breaking:
use:
- FILE # safest default for internal services
# - WIRE_JSON # use if you serialize to JSON (grpc-gateway, Connect)
# - PACKAGE # use if you publish proto to external consumers
The full template is at ${CLAUDE_PLUGIN_ROOT}/templates/buf.yaml.
| Category | Blocks | Use when |
|---|---|---|
FILE | Any change that breaks within a single .proto file | Internal services |
PACKAGE | Any change that breaks within a package (across files) | Shared packages |
WIRE | Only wire-level breaks (tag reuse, type changes) | Very mature schemas |
WIRE_JSON | WIRE + JSON-name changes | When using grpc-gateway / Connect JSON |
Recommendation for Toru's projects: start with FILE, upgrade to WIRE_JSON once the service is in production with external clients.
On every .proto file Write/Edit, post-edit-lint.js:
buf format -w <file> (silent auto-fix)buf lint --path <file> (collect style violations)buf breaking --against .git#branch=main --path <file> (CRITICAL — catches wire breaks)additionalContextmessage User {
reserved 3, 5 to 7; // NEVER reuse these numbers
reserved "old_name", "legacy_id"; // NEVER reuse these names
string id = 1;
string name = 2;
// field 3 is reserved (was: string email)
string display_name = 4;
// fields 5-7 are reserved
int64 created_at = 8;
}
Rules:
reserved: use reserved N; and reserved "old_name";If you use Connect RPC:
buf lint DEFAULT enforces this via RPC_REQUEST_STANDARD_NAME)google.rpc.Status or Connect's error model, not custom enumsbuf breaking needs a git baseline: the hook runs buf breaking --against .git#branch=main. If your repo uses master or develop as main, the hook detects automatically.proto/ directory, not scattered across service dirs. buf.yaml expects a module root.package.foo.v1 and package.foo.v2 in the same file: split into v1/*.proto and v2/*.proto directories.reserved numbers are free: always reserve when deleting, even if you're "sure" you'll never need them back.// BEFORE
message User {
string email = 3; // field 3 is a string
}
// AFTER (THIS WILL BE BLOCKED BY buf breaking)
message User {
int64 email = 3; // ❌ WIRE BREAKING: type changed on field 3
}
// CORRECT WAY
message User {
reserved 3; // retire the old field number
reserved "email"; // retire the old field name
int64 email_id = 9; // new field, new number, different name
}
edit-lint-feedback-loop — hook mechanicsarchitecture-enforcement — broader architecture enforcement patternsharness-setup — initial setupSee gotchas.md.