Repository navigation
ci(proto-drift): drift checks for go/py/dart/ruby/elixir stubs + regenerate stale stubs - #315
Conversation
The byte-range fields (offset/length on StreamDataRequest and StreamPublicDataRequest) were regenerated only for go/swift/rust/kotlin. Regenerate the Python, Dart, Ruby and Elixir stubs from antd/proto with the generator versions that produced the committed files (grpcio-tools 1.80.0, protoc_plugin 22.3.0, grpc-tools gem 1.84.0, protoc-gen-elixir 0.16.0), so every unchanged proto reproduces byte for byte and the diff is only real drift: - data: offset/length in all four SDKs. - dart upload.pb.dart: the resumable-finalize doc comments on FinalizeUploadRequest, which the Dart stubs never picked up. - dart wallet.pbgrpc.dart: import alias $6 -> $7. protoc_plugin numbers the aliases with one counter across all files of a protoc call; the committed file came from a run over a different set of files (later regenerations reverted the renumbering as noise), so no full-set run reproduced it. The aliases are file-local, so this changes nothing at runtime. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven SDKs commit generated protobuf stubs, but only swift, rust and kotlin were checked against antd/proto, so stale stubs in the other five went unnoticed (the byte-range fields, for one). Give antd-go, antd-py, antd-dart, antd-ruby and antd-elixir one regen script each that pins the generator versions reproducing the committed stubs and lists the protos that SDK ships, and add a proto-drift job per SDK that installs those versions, runs the same script and fails on any diff or new file: - antd-go/scripts/generate-protos.sh (new): protoc 34.1, protoc-gen-go v1.36.12, protoc-gen-go-grpc v1.6.2 (the plugins are built by the script). - antd-py/scripts/generate_proto.py: grpcio-tools 1.80.0; an explicit proto list instead of every file under antd/proto (verify.proto was never shipped); LF output on every OS. - antd-dart/tool/generate_proto.sh: protoc_plugin 22.3.0. - antd-ruby/scripts/generate-protos.sh (new): grpc-tools gem 1.84.0. - antd-elixir/scripts/generate-protos.sh (new): protoc-gen-elixir 0.16.0. The scripts refuse to run with other generator versions, so CI and a developer's machine cannot silently diverge. Each clears its stub directory first, so a proto dropped from the list shows up as deleted stubs. The SDK READMEs now point at the scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dirvine
left a comment
There was a problem hiding this comment.
Six-seat independent review panel — PR #315 (proto drift checks)
Reviewed head: 20a04064786f35e9faf7fd716885725d0b3f1eba (branch nic/v2-1503-proto-drift-checks → base nic/v2-1425-antd-byte-range-download). 19 files, +631/−123.
Summary
Adds a proto-drift GitHub workflow with five jobs (Go, Python, Dart, Ruby, Elixir) that regenerate each SDK's committed protobuf stubs with pinned generator toolchains and fail on any drift (modified, deleted, or newly untracked files). Also regenerates the stale py/dart/ruby/elixir data stubs to pick up the base branch's new offset/length range fields, and fixes the Dart wallet alias ($6 → $7). All six seats found no blocking defects introduced by the diff; the regenerated stubs were verified consistent with antd/proto by descriptor-level comparison in multiple seats.
Per-seat verdicts
| Seat | Tool | Verdict | Key findings |
|---|---|---|---|
| 1 | gpt-5.1-codex (via OpenRouter) | APPROVE-WITH-CONCERNS | Triggers cover this stacked PR (PR trigger has no base-branch restriction); non-main pushes without a PR don't run the workflow (pre-existing pattern). Generator pins are tighter than the full execution environment (Go/Dart/Ruby SDK versions float; Go go.mod changes are absent from the path filter) |
| 2 | gpt-5.1-codex (via OpenRouter) | APPROVE-WITH-CONCERNS | Dart guard checks the activated package but protoc resolves the plugin via PATH (local bypass; CI mitigates at the cache bin dir). Elixir script doesn't enforce the formatter's Elixir build. Python failure message recommends a non-executable command |
| 3 | gpt-5.1-codex (via OpenRouter) | APPROVE | Decoded Python descriptor bytes: exactly offset/length (optional uint64, fields 3/4, synthetic oneofs) added to the two streaming requests; Ruby descriptor bytes byte-identical to Python's; Go stamps and layout consistent; no consumer breakage |
| 4 | gpt-5.1-codex (via OpenRouter) | APPROVE | Dart descriptors/JSON, upload doc comments, and wallet alias change all verified against the protos; no breaking changes; release-doc bookkeeping only |
| 5 | gpt-5.1-codex (via OpenRouter) | APPROVE-WITH-CONCERNS | Elixir README documents the regen command from the wrong working directory (introduced by this diff); Python drift error's suggested command is not executable (file mode 100644); Dart plugin PATH-selection gap. Stack ordering on the feature base verified; no workflow overlap with #316/#317 |
| 6 | GLM-5.2 (OpenRouter, independent model seat) | APPROVE | Stubs correctly regenerated; all five scripts pin and enforce generator versions; CI jobs sound. Non-blocking: float Dart/Ruby SDK selectors are self-correcting but could be pinned; PROTOC_VERSION env only strictly enforced by the Go script |
Dissent: none. All six seats approve; three seats carry non-blocking concerns, none of which is a defect in the generated stubs or a broken gate.
Blocking issues
None.
Non-blocking findings (deduplicated across seats)
- Python drift error suggests a non-executable command —
.github/workflows/proto-drift.yml:235setsREGENto the script path alone (file mode100644); line 243 tells developers to run it directly. Usepython antd-py/scripts/generate_proto.py, matching the actual invocation at line 230. (Seats 2, 5) - Dart plugin selection —
antd-dart/tool/generate_proto.sh:31checks the activated pub package, but line 45 letsprotocfindprotoc-gen-dartvia PATH; a different installation earlier on PATH could pass the guard. CI mitigates by prepending the cache bin dir (proto-drift.yml:269). Bind--pluginto the checked installation. (Seats 2, 5) - Elixir README working directory —
antd-elixir/README.md:103invokesantd-elixir/scripts/generate-protos.sh, but the preceding sequence runs insideantd-elixir; following it fails with "No such file or directory". Introduced by this diff's replacement command. (Seat 5) - Elixir formatter build not enforced locally —
antd-elixir/scripts/generate-protos.sh:25pins the plugin (0.16.0) but not the Elixir version the escript was built with; CI uses 1.18.4 (proto-drift.yml:336). Document the canonical escript build environment to avoid formatting-only local/CI drift. (Seat 2) - Trigger coverage — pushes to non-main branches without a PR do not run the workflow (pre-existing pattern, main-only push trigger); new SDK roots or
lib/antd/v2paths would need filter extensions; GitHub's 300-file path-filter limit is a pre-existing platform constraint. (Seat 1) - Floating SDK selectors — Dart
stableand Ruby3.3float within their series; the drift check is self-correcting (any output change fails the job), so this is flakiness-risk, not false-green risk. (Seats 1, 6) - Release bookkeeping — the new
offset/lengthrequest fields are absent fromantd-dart/CHANGELOG.md; document at release. Not a merge blocker. (Seat 4)
Claim checks (highlights)
- Stubs match the protos — verified by independent descriptor decoding (seat 3: Python/Ruby 1,564-byte FileDescriptorProto blobs byte-identical, fields 3/4 optional uint64 with synthetic oneofs; seat 4: Dart JSON descriptors, Elixir declarations, and upload doc comments all match). No seat found a mismatch.
- "The old Dart script reverted alias changes" (PR body) — refuted as written by seats 2 and 5: the base script contains no revert/normalization logic. This does not affect the correctness of the current
$6→$7change, which both seats verified is a pure alias rename (43 occurrences, symbols and RPC paths unchanged). - Explicit proto lists and cleanup globs cover exactly the committed stub inventory — verified against
git ls-tree(seat 2, seat 3): no under-generation or over-generation at HEAD;--exclude-standardhides no committed output paths. - Byte-for-byte regeneration and negative-test claims — unverifiable by static inspection; the five drift jobs are reported green on this exact head and are accepted as supplied evidence.
Limitations
- All seats were read-only: no generators, installations, tests, or network calls were run by the panel. Execution evidence is the CI run on head
20a0406(all five drift jobs green). - Seats 1–5 ran via OpenRouter (gpt-5.1-codex) rather than the Codex CLI: the Codex CLI's ChatGPT auth on this machine is revoked (401 refresh-token-invalidated), and the CLI now requires the Responses wire API for custom providers — a panel-infra note, not a PR finding.
Verdict
APPROVE — no blocking issues; seven non-blocking follow-ups, the two worth addressing in-branch before merge being #1 (one-line REGEN fix) and #3 (README path).
Summary
Follow-up to #313 (review note 4). Seven SDKs commit generated protobuf stubs, but only three were checked against
antd/protoin CI: swift and kotlin inproto-drift.yml, rust intests/proto_drift.rs. The Go stubs drifted unnoticed onmain, and #313 had to catch them up in passing. #313 also left the py, dart, ruby and elixirdatastubs without its newoffset/lengthfields.This PR regenerates the stale stubs in a stub-only commit, and adds a drift job for each of go, py, dart, ruby and elixir.
Stacked on #313. The base is
nic/v2-1425-antd-byte-range-download, because the regenerated stubs include #313'sdata.protochange. Merge after #313. If #313 is merged with a merge commit, GitHub retargets this PR tomainonce that branch is deleted.Commits
chore(sdks): regenerate stale py/dart/ruby/elixir stubs(stub files only):offset/lengthonStreamDataRequestandStreamPublicDataRequest, in py (data_pb2.py,.pyi), dart (data.pb.dart,data.pbjson.dart), ruby (data_pb.rb) and elixir (data.pb.ex).upload.pb.darthad never picked up the resumable-finalize doc comments onFinalizeUploadRequest.wallet.pbgrpc.dart: an import alias changes from$6to$7, which has no runtime effect. protoc_plugin numbers these aliases with one counter across all the files in a single protoc call. The committed file came from a run over a different set of files, and earlier regens reverted alias-only changes by hand as noise, so no full regen could reproduce it. After this one-time update the output is deterministic, and the check is strict with no tolerance rules.ci(proto-drift): check go/py/dart/ruby/elixir stubs against antd/proto:One regen script per SDK, which developers and CI both run:
antd-go/scripts/generate-protos.sh(new)antd-py/scripts/generate_proto.pyantd-dart/tool/generate_proto.shantd-ruby/scripts/generate-protos.sh(new)antd-elixir/scripts/generate-protos.sh(new)Each one pins its generator versions and refuses to run with any other. It clears its stub directory first, so a removed proto shows up as a deletion. It also generates an explicit list of protos, matching what that SDK already ships. Previously the py script globbed every proto, which created untracked
verify_pb2*files.Five jobs in
proto-drift.yml, one per SDK, modeled on the swift job. Each installs the pinned tools, runs the script, and fails on a diff or a new file. They're triggered onantd/proto/**plus each SDK's stub path and script.READMEs: each SDK's README gets the regen command.
Pinned generator versions
For every unchanged proto, regenerating with these versions reproduces the committed stubs byte for byte.
GRPC_GENERATED_VERSIONandProtobuf Python Version: 6.31.1in the committed filesgrpc_tools_ruby_protoc _1.84.0_The workflow's install steps repeat these pins. If someone bumps one without the other, the script's version check fails CI loudly.
Test evidence
proto-drift.yml, dispatched on this branch (run 38232458413), passes all 7 jobs, the 5 new ones in 6–28 s each. actionlint (with shellcheck) is clean on the workflows, and shellcheck is clean on the scripts.string drift_probe = 99;toCostincommon.protowithout regenerating fails all 5 new jobs at "Fail on drift", each showing only its common stubs. Adding a proto to a script's list fails on new untracked files.go vetandgo test ./...pass.dart analyze --fatal-infosis clean; 137 tests pass.main(b38c3d3): the py, ruby and elixir checks pass. Go fails on the drift feat(antd): byte-range downloads on the data streams (REST, gRPC, antd-go) #313 already fixes. Dart fails on the upload comment and the alias numbering, which this PR fixes. Somainis clean once feat(antd): byte-range downloads on the data streams (REST, gRPC, antd-go) #313 and this PR are both merged.Notes
verify.protostubs, as before; a new proto is added to an SDK by listing it in its script. The kotlin copy works the same way.🤖 Generated with Claude Code