Skip to content

ci(proto-drift): drift checks for go/py/dart/ruby/elixir stubs + regenerate stale stubs - #315

Merged
Nic-dorman merged 2 commits into
mainfrom
nic/v2-1503-proto-drift-checks
Oct 12, 2026
Merged

Nic-dorman merged 2 commits into
mainfrom
nic/v2-1503-proto-drift-checks

Conversation

@Nic-dorman

@Nic-dorman Nic-dorman commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Summary

Follow-up to #313 (review note 4). Seven SDKs commit generated protobuf stubs, but only three were checked against antd/proto in CI: swift and kotlin in proto-drift.yml, rust in tests/proto_drift.rs. The Go stubs drifted unnoticed on main, and #313 had to catch them up in passing. #313 also left the py, dart, ruby and elixir data stubs without its new offset / length fields.

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's data.proto change. Merge after #313. If #313 is merged with a merge commit, GitHub retargets this PR to main once that branch is deleted.

Commits

  1. chore(sdks): regenerate stale py/dart/ruby/elixir stubs (stub files only):
    • Range fields: offset / length on StreamDataRequest and StreamPublicDataRequest, in py (data_pb2.py, .pyi), dart (data.pb.dart, data.pbjson.dart), ruby (data_pb.rb) and elixir (data.pb.ex).
    • Older drift: dart upload.pb.dart had never picked up the resumable-finalize doc comments on FinalizeUploadRequest.
    • Dart wallet.pbgrpc.dart: an import alias changes from $6 to $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.
  2. 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.py
      • antd-dart/tool/generate_proto.sh
      • antd-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 on antd/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.

SDK Generator Source of the pin
go protoc 34.1, protoc-gen-go v1.36.12, protoc-gen-go-grpc v1.6.2 The headers of all 17 committed files. A full regen on this branch gives zero diff.
py grpcio-tools 1.80.0 (bundles protoc 31.1) GRPC_GENERATED_VERSION and Protobuf Python Version: 6.31.1 in the committed files
dart protoc_plugin 22.3.0 Output is identical with protoc 3.21, 25.3 and 34.1
ruby grpc-tools 1.84.0 (bundles protoc 35.1) Invoked as grpc_tools_ruby_protoc _1.84.0_
elixir protoc-gen-elixir 0.16.0, Elixir 1.18.4 / OTP 27 in CI Version stamped in the code. Output goes through the Elixir formatter, and is identical across escripts built with Elixir 1.18.4 and 1.19.5.

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

  • CI: 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.
  • Negative test (not committed): adding string drift_probe = 99; to Cost in common.proto without 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.
  • SDK tests after the regen, on Linux:
    • go: go vet and go test ./... pass.
    • py: pytest gives 363 passed, 7 skipped.
    • dart: dart analyze --fatal-infos is clean; 137 tests pass.
    • ruby: 127 runs, 0 failures.
    • elixir: 155 tests, 0 failures.
  • Against 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. So main is clean once feat(antd): byte-range downloads on the data streams (REST, gRPC, antd-go) #313 and this PR are both merged.

Notes

  • New protos are opt-in per SDK. py, dart, ruby and elixir still don't ship verify.proto stubs, as before; a new proto is added to an SDK by listing it in its script. The kotlin copy works the same way.
  • Comment-only proto changes fail the go, dart and swift jobs. Py, ruby and elixir stubs carry no comments.

🤖 Generated with Claude Code

Nic-dorman and others added 2 commits October 12, 2026 09:43
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 dirvine left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

  1. Python drift error suggests a non-executable command — .github/workflows/proto-drift.yml:235 sets REGEN to the script path alone (file mode 100644); line 243 tells developers to run it directly. Use python antd-py/scripts/generate_proto.py, matching the actual invocation at line 230. (Seats 2, 5)
  2. Dart plugin selection — antd-dart/tool/generate_proto.sh:31 checks the activated pub package, but line 45 lets protoc find protoc-gen-dart via PATH; a different installation earlier on PATH could pass the guard. CI mitigates by prepending the cache bin dir (proto-drift.yml:269). Bind --plugin to the checked installation. (Seats 2, 5)
  3. Elixir README working directory — antd-elixir/README.md:103 invokes antd-elixir/scripts/generate-protos.sh, but the preceding sequence runs inside antd-elixir; following it fails with "No such file or directory". Introduced by this diff's replacement command. (Seat 5)
  4. Elixir formatter build not enforced locally — antd-elixir/scripts/generate-protos.sh:25 pins 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)
  5. 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/v2 paths would need filter extensions; GitHub's 300-file path-filter limit is a pre-existing platform constraint. (Seat 1)
  6. Floating SDK selectors — Dart stable and Ruby 3.3 float 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)
  7. Release bookkeeping — the new offset/length request fields are absent from antd-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 → $7 change, 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-standard hides 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).

@Nic-dorman
Nic-dorman changed the base branch from nic/v2-1425-antd-byte-range-download to main October 12, 2026 12:23
@Nic-dorman
Nic-dorman merged commit 85f64f5 into main Oct 12, 2026
22 checks passed
Nic-dorman added a commit that referenced this pull request Oct 13, 2026
Follow-ups from the #315–#317 reviews (drift scripts, range error tests and docs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants