# Proto Gate > Paste a Protocol Buffers schema — and, for the compatibility lane, the previous revision of the > same file beside it — and work that schema through four lanes: a schema review against the rules > protoc and a schema registry actually enforce, a compatibility gate that classifies every > difference three ways (what it does to the wire, to canonical JSON, and to generated source), an > RPC contract review of every method, and the rollout plan with an observable gate on every step. > A free in-browser proto parser reads the file before any model runs. Live at https://proto-gate.skillsafe.ai/ · API tutorial at https://proto-gate.skillsafe.ai/api.html ## What problem it solves Almost every dangerous change to a `.proto` file compiles cleanly. Renaming a field is invisible on the wire and fatal for every JSON consumer. Renumbering one is the exact reverse: JSON is fine and the binary value silently arrives unset, with no error on either side. Deleting a field without a `reserved` statement hands its number back to the pool, so a future edit can point a different type at bytes that are still sitting in a queue. Removing an rpc is not a build failure for a client that has already shipped — it is an `UNIMPLEMENTED` on the first call after the deploy. None of that shows up in `protoc` output, and only some of it shows up in a breaking-change checker. This app reads the schema, and the change, and says which category each problem is in — and then says what to do about it. ## The work object One `.proto` file, pasted or dropped. Optionally a second paste: the previous revision of the *same* file, which is what the compatibility lane compares against. The diff is computed symbol by symbol from two parse trees, so reformatting, reordering declarations and reindenting produce no changes at all. Separate files in one paste are marked with a line reading `// file: `; with no marker the whole paste is read as one file. Imports are **not** resolved. A type that lives in another file is reported as unresolvable rather than guessed at, and the well-known `google.protobuf.*` types are the only ones the reader knows without being shown. ## The four lanes | Lane id | What it answers | Postures | | --- | --- | --- | | `schema` | Is this schema sound, and will it still be workable in two years? | `schema-clean`, `schema-fixable`, `schema-unsound` | | `compat` | If this merges, what breaks — and for whom? | `wire-safe`, `wire-risky`, `wire-breaking` | | `contract` | Could somebody operate this service at three in the morning? | `contract-solid`, `contract-thin`, `contract-unsafe` | | `rollout` | What sequence gets this into production without an incident? | `rollout-ready`, `rollout-staged`, `rollout-blocked` | Every lane takes the same schema. The handoff is a button: the schema review names the declarations, the compatibility gate says which of those edits an already-running peer cannot absorb, the contract review says which method needs a field, and the rollout plan sequences whatever survives. `compat` is the only lane that requires the previous revision, and it is disabled with a reason rather than run against nothing. ## The free lane — what runs in the browser with no account and no credits The reader is not a preview of the paid lanes; it is the accountability record for them. Every flag it raises is sent into the run and must come back addressed, and the result page shows any flag the model failed to mention. - **A real parser, not a grep.** A tokenizer and recursive-descent parser for proto2, proto3 and editions: nested messages, `oneof`, `map`, `reserved` numbers/ranges/names, enums with `allow_alias`, `extend`, groups, services, and rpcs with their `stream` modifiers. A half-edited file still parses on a best-effort basis with the first bad line named. - **Field numbering.** Duplicates within a message. The 19000–19999 block protobuf reserves for its own implementation, which protoc refuses outright. Numbers outside 1–536870911. A number that is both `reserved` and in use. Numbers above 15 paying a second tag byte on every message while one-byte numbers sit free — with the free numbers listed. - **The enum zero value.** proto3 cannot distinguish an unset field from a zero one, so whatever sits at zero becomes the value every message that never set the field appears to carry. An enum whose zero is `PENDING` is an enum where every unset order is pending. Also: no zero value at all, a non-zero first value, duplicate numbers without `allow_alias`, `allow_alias` with no aliases, and values not prefixed with the enum name (which is a C++ scoping collision, not a style opinion). - **Legality protoc enforces.** A map key that is not an integral or string type. A `repeated` map. A repeated map value. A `repeated` field inside a `oneof`. `required`, which can never be removed. - **Semantics that only bite later.** Money in a `float` or `double`. A timestamp as a bare `int64`. A duration as a bare integer whose unit lives in the field name. Wrapper types where proto3 `optional` now does the same job. A single-member `oneof`, which makes every later addition a breaking change for anything switching on it. A `deprecated` comment with no `[deprecated = true]`. - **Service shape.** `google.protobuf.Empty` in a signature, which has no room to grow. A request message shared between methods, so a field added for one is added to all. A `List` rpc returning an unbounded `repeated` field with no paging fields, which is one growth spurt from the 4 MB default gRPC message limit. Bidirectional streaming, which is the hardest pattern to operate. - **Package and imports.** No package. A package with no version suffix, so there is nowhere to put a v2. A referenced well-known type with no import behind it. An import nothing uses. `import weak`. - **The wire-compatibility differ.** With both revisions pasted, every difference is classified for the wire, for canonical JSON and for generated source, independently: a field renumbered, removed with or without `reserved`, renamed, retyped (inside a wire class, with the exact value that does not survive it, or across classes), a cardinality change, a field moving into or out of a `oneof`, a changed `json_name`, a new field taking a previously reserved number, an enum value removed or renumbered or renamed, a message or service removed, an rpc removed or resignatured, a changed streaming mode, a changed package. - **The tag-byte table.** Every field, its number, its presence, its canonical JSON key, and what its number costs on every message. - **The delta as you fix things.** Which flags cleared, which are still open and which are new since the reviewed baseline, counted live and free. ## The API contract Base URL `https://api.skillsafe.ai/v1/app-api`. The run input is the object itself — do not wrap it in an `input` key: ```json { "task": "compat", "proto": "syntax = \"proto3\";\npackage acme.orders.v1;\n...", "previous": "syntax = \"proto3\";\npackage acme.orders.v1;\n...", "notes": "merging Thursday; forty services and two mobile apps are on v1", "prescan": { "facts": {}, "flags": [], "rules": [], "changes": [] } } ``` `task` selects the lane and decides the shape of the reply. `previous` is only meaningful to `compat` (and informs `rollout`). `prescan` is optional but strongly recommended: without it the model has nothing to reconcile against and the reconciliation table comes back empty. Every lane returns one JSON object with the same envelope — `task`, `title`, `posture`, `confidence`, `verdict`, `exec_summary`, `findings[]`, `coverage_check[]`, `artifacts[]`, `assumptions[]`, `open_questions[]`, `next_steps[]`, `summary` — plus exactly one lane array: `rules` for `schema`, `changes` for `compat`, `methods` for `contract`, `steps` for `rollout`. Finding ids are prefixed per lane: `SC-`, `CP-`, `CT-`, `RO-`. Full field lists, allowed enum values and one worked example per lane are in [api.html](https://proto-gate.skillsafe.ai/api.html). ## Model and cost `gpt-terra` (currently `gpt-5.6-terra`), publisher markup 1000 bps. `estimate` is free and per-lane; the hold prices the full output cap and the actual charge is usually far lower. Signed-in users only for a run; the whole free reader, both bundled examples and every saved example result work before sign-in. ## Limits worth knowing - Nothing here compiles your schema. There is no `protoc`, no `buf` and no registry behind the page — every judgement is a read of the text you pasted, and the reader's numbers are computed in your browser. - Imports are not resolved, so a rule that depends on a type defined elsewhere is reported as an open question rather than answered. - A compatibility verdict is about the *schema*. It cannot know whether a client exists that reads a given field, and it says so: the `who_breaks` column names a direction and a party, not a specific service in your fleet. - Custom options are parsed but not interpreted, and a `buf.yaml` lint or breaking configuration is not read. A clean result here is not a substitute for running your own build and your own breaking-change check in CI. - Proto2, proto3 and editions are all parsed; the guidance is written for proto3 and editions, with proto2 hazards (`required`, groups, closed enums) named where they appear. ## Source skills A derived work built on five published agent skills about Protocol Buffers and gRPC, credited in the app, in its footer and in its system prompt: - `@bufbuild/protobuf` — Buf's own protobuf skill: schema layout, lint rules and the breaking-change categories the compatibility lane's three columns follow. - `@samber/golang-grpc` — gRPC usage guidelines, proto organisation, status codes, interceptors, TLS and streaming patterns for production services. - `@manutej/grpc-microservices` — protobuf schema design, service definitions, streaming patterns and production gRPC architecture. - `@bobmatnyc/golang-grpc` — proto layout, codegen, deadlines, error codes, streaming, health checks and testing. - `@aj-geddes/grpc-service-development` — end-to-end gRPC service development. None of them is republished here and none of them is executed.