Skip to content

build(codegen): own the whatspec codegen and regenerate at 2.3000.1044659339 - #1293

Merged
jlucaso1 merged 5 commits into
mainfrom
claude/whatspec-codegen-crate-mmum1o
Aug 12, 2026
Merged

build(codegen): own the whatspec codegen and regenerate at 2.3000.1044659339#1293
jlucaso1 merged 5 commits into
mainfrom
claude/whatspec-codegen-crate-mmum1o

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

whatspec stopped committing Rust. generated/**/*.rs is gitignored there and only the language-neutral IR ships, so "refreshing a vendored file is a cp" has been false for a while and nothing checked that the vendored set stayed coherent. It did not: abprops.rs and appstate/schemas.rs described WhatsApp 2.3000.1042742319 while mex_operations.rs and tokens.json described 2.3000.1044659339, and the default app version a fresh device announced was a fourth hand-maintained copy of the same number.

This adds tools/whatspec-codegen, a non-published workspace member that reads the IR at a pinned commit and emits every derived file in one pass, and regenerates the whole set at 2.3000.1044659339.

Design

Acquisition is git, not HTTP. The lock pins a commit; fetching that commit reuses whatever credentials, proxy and CA configuration the user's git already has, so the tool works wherever git clone does. The checkout is sparse to generated/ (~10 MB, cached under target/). Per-file sha256 digests are checked on top, because they are also what makes a local --from checkout trustworthy. No new dependencies: anyhow, serde, serde_json, sha2, all already in the tree at the same versions.

Regeneration is all-or-nothing. Every domain document has to stamp the same waVersion — including proto/WAProto.proto, which carries its stamp as a comment rather than a JSON envelope and is therefore the one domain whose drift could otherwise reach whatsapp.proto with every registry agreeing. The manifest has to agree, and the IR schemaVersion major has to be one this tool reads. A tree assembled from two whatspec runs stops the run rather than producing the drift this PR removes.

Reimplemented rather than depended on. Depending on whatspec's wa-codegen from git would put a git source in the workspace lockfile and four more crates in every cargo clippy --workspace, to get output that is explicitly untracked and "reference" quality upstream. The emitters here are ~700 lines and we control the shape.

Local additions stay declarative. waproto/src/whatsapp.proto carries two messages WA dropped from the public bundle (LIDMigrationMapping, LIDMigrationMappingSyncPayload). Those now live in LOCAL_BLOCKS in the proto emitter, spliced after a named anchor, alongside the existing LOCAL_FIELDS hook in waproto/build.rs for single fields. A sync that reclaims one of those names fails the build instead of shadowing it, and a sync that removes the anchor fails instead of silently dropping the block.

cargo run -p whatspec-codegen                                  # regenerate from the pinned commit
cargo run -p whatspec-codegen -- --check                       # fail if the tree drifted
cargo run -p whatspec-codegen -- --from ../whatspec/generated  # use a local checkout
cargo run -p whatspec-codegen -- --update-lock --rev main      # move to a newer bundle

Writing the .proto also reruns scripts/regenerate-proto-desc.sh, so protoc (the PROTOC_VERSION in main.yml, 3.25.3) has to be on PATH; --skip-proto-desc defers it. --check never rebuilds the descriptor — it compares the two hashes recorded in whatsapp.desc.sha256 instead, which is why the CI job needs only rustfmt and git and still catches a tree regenerated with --skip-proto-desc.

Correctness of the reimplementation

The emitters have to keep mapping bundle keys to the same public names, or a refresh silently renames API. Proven, not asserted: pinning the tool at the older whatspec commit (2.3000.1042742319) and running --check reproduces the currently committed abprops.rs, appstate/schemas.rs and whatsapp.proto byte for byte, local proto block included. The three that did not match under that pin are exactly the three already known to be from the newer bundle, and they match byte for byte when pinned to it. The descriptor is reproducible too: regenerating whatsapp.desc from the unchanged .proto with protoc 25.3 gives the committed sha256.

Changes

  • tools/whatspec-codegen (new, publish = false, outside default-members): IR model, naming, six emitters, git acquisition with a sha256 lock, --check.
  • Regenerated at 2.3000.1044659339: wacore/src/iq/abprops.rs, wacore/src/iq/mex_operations.rs (already there, unchanged), wacore/appstate/src/schemas.rs, wacore/binary/src/tokens.json, waproto/src/whatsapp.proto + .desc + .desc.sha256.
  • wacore/src/version/generated.rs (new, generated) carries WA_WEB_VERSION / WA_WEB_VERSION_STR, re-exported from wacore::version and whatsapp_rust::version. Device::default uses it, so the announced version and the vendored registries can no longer disagree.
  • abprops gains the WAWebHybridABPropsConfigs registry as abprops::hybrid (339 flags); the web registry goes 2036 to 2193. Every flag this client reads survives, and both props::stale entries are still absent upstream.
  • appstate drops DeviceCapabilities, adds LabelSublist, and moves enum-field metadata between MarketingMessage, WasaRootSecret and StatusPrivacy. schemas_unlisted::LABEL_MESSAGE is still absent from the generated registry, so it stays where it is.
  • CI gains a Generated Artifacts job running --check.
  • AGENTS.md and agent_docs/wa_web_reference.md updated: the cp instruction was wrong, and the generated-vs-hand-written boundary is now written down where someone about to edit a generated file will read it.

Breaking changes

cargo-semver-checks reports these against the last published release — all of them upstream schema changes carried through by the regeneration, none of them referenced anywhere in this workspace:

  • waproto: upstream renamed the AIRichResponseContentItemsMetadata oneof, so a_i_rich_response_content_item becomes ai_rich_response_content_item. Migration is the rename.
  • waproto: upstream removed EncryptMessageOutput.messageKey (field 6), so EncryptMessageOutput::message_key and its …View counterpart are gone. Nothing in this crate constructs or reads that message; a downstream consumer that does needs to drop the field.
  • wacore: DeviceProps.PlatformType::WAIL is new. It maps to OtherWebClient for the same reason the other non-browser platforms do: the Android letters need attestation this crate cannot produce, and '0' is server-rejected. A downstream match on PlatformType needs the new arm.

The Semver Checks (informational) job is continue-on-error: true and reports exactly the two waproto removals above (6 findings = 2 fields × struct and View, some listed twice).

Guarantees

  • tools/whatspec-codegen/tests/committed_artifacts.rs is the offline half of the gate: it needs no whatspec checkout, so it runs in the ordinary matrix and catches the failure that actually happened, a single domain refreshed on its own. It also checks the token tables still start with the reserved empty slot, since tokens.json carries no version stamp and an off-by-one there corrupts every frame.
  • 59 unit tests cover naming and each emitter, each with its failure case: an unknown index-part tag, a non-finite float default, a lock pinning a branch, a tampered IR file, an unparseable waVersion, a proto anchor that vanished, a proto name upstream reclaimed, a proto stamped from another build, an IR not grouped by module, a collection or scope the enum never declared, and two wire strings that normalize to one Rust identifier.

Cost

The proto grew by ~19 KB of descriptor and the abprops registry by ~490 flags. The flags are pub const and unreferenced ones emit nothing, but the extra proto messages will move the binary-size gate; if the increase reads as expected, it needs the size-increase-ok label.

Validation

cargo fmt --all -- --check
cargo test --workspace --exclude e2e-tests --exclude voip-cli
cargo clippy --workspace --all-targets -- -D warnings
cargo doc --workspace --no-deps          # RUSTDOCFLAGS=-D warnings
cargo run -p whatspec-codegen -- --check # full pinned path, git fetch included

All green locally. CI status is on the checks tab; the only expected failure there is Semver Checks (informational), for the breaks listed above.

claude added 2 commits August 12, 2026 03:05
…ed IR artifacts

whatspec stopped committing Rust: generated/**/*.rs is gitignored there and only
the language-neutral IR ships, so refreshing a vendored file is no longer a `cp`
and nothing checked that the vendored set stayed coherent. It did not: abprops
and appstate described WhatsApp 2.3000.1042742319 while mex and the token tables
described 2.3000.1044659339, and the default app version a fresh device
announced was a fourth hand-maintained copy of the same number.

whatspec-codegen reads the IR at a commit pinned in whatspec.lock.json and emits
every derived file in one pass, refusing to run when the domains disagree on a
build. Acquisition is git rather than HTTP so it works wherever `git clone`
does, sparse to `generated/`, with the per-file sha256 checked on top so a local
`--from` checkout is held to the same bar. `--check` reproduces the tree
byte for byte and is wired into CI.

The emitters reproduce the shapes already committed, proven by regenerating from
the older pinned IR and getting the current files back unchanged. Local
additions stay declarative: a retained proto message lives in LOCAL_BLOCKS
beside the LOCAL_FIELDS hook, and a sync that reclaims the name fails the build
instead of shadowing it.
….1044659339

One pass of whatspec-codegen, so the whole set now describes one build instead
of two. The version itself is generated as wacore::version::WA_WEB_VERSION and
is what a fresh device announces, which removes the last hand-maintained copy of
the number.

What moved with the bump:

- abprops gains the WAWebHybridABPropsConfigs registry (339 flags) as
  `abprops::hybrid`, and the web registry goes 2036 -> 2193. Every flag this
  client reads survives, and both `props::stale` entries are still absent
  upstream, so the module stays correct.
- appstate drops DeviceCapabilities, adds LabelSublist, and moves enum-field
  metadata between MarketingMessage, WasaRootSecret and StatusPrivacy.
  schemas_unlisted::LABEL_MESSAGE is still absent from the registry, so it stays.
- The proto picks up DeviceProps.PlatformType::WAIL, mapped to the
  OtherWebClient companion type for the same reason the other non-browser
  platforms are: it needs attestation this crate cannot produce, and '0' is
  server-rejected.
- The proto also renames the AIRichResponseContentItemsMetadata oneof upstream,
  so waproto's `a_i_rich_response_content_item` becomes
  `ai_rich_response_content_item`. Breaking for anyone matching that oneof by
  name; migration is the rename.
- tokens.json is unchanged apart from gaining its missing trailing newline.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 269a0871-487c-4df6-949b-cede60bca71a

📥 Commits

Reviewing files that changed from the base of the PR and between bf18d4d and a6e1c7b.

📒 Files selected for processing (2)
  • tools/whatspec-codegen/src/main.rs
  • tools/whatspec-codegen/src/source.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for expanded WhatsApp capabilities, including AI provenance, bot history sharing, music and status metadata, identity verification, contact refresh, account attestation, and co-existence synchronization.
    • Added new message types, synchronization actions, and encryption-related operations.
    • Added the LabelSublist app-state schema and automatic WhatsApp Web version handling.
    • Added tooling to regenerate and verify protocol and schema artifacts.
  • Bug Fixes

    • Improved WAIL companion-platform handling.
    • Updated protocol and generated artifacts for consistency and reliability.
  • Documentation

    • Expanded guidance for regenerating and validating generated artifacts.

Walkthrough

Added the whatspec-codegen tool. It loads pinned IR, validates lock data and versions, generates repository artifacts, and checks drift in CI. The change also updates WhatsApp protocol, AppState, token, and version artifacts.

Changes

Whatspec code generation

Layer / File(s) Summary
IR source and generation contracts
tools/whatspec-codegen/Cargo.toml, tools/whatspec-codegen/src/ir.rs, tools/whatspec-codegen/src/source.rs, tools/whatspec-codegen/src/naming.rs, tools/whatspec-codegen/whatspec.lock.json, AGENTS.md, agent_docs/wa_web_reference.md
Adds IR models, pinned source loading, SHA-256 verification, naming helpers, lock metadata, and regeneration guidance.
Generated artifact emitters
tools/whatspec-codegen/src/emit/*
Adds emitters for A/B properties, AppState schemas, MEX operations, protobuf blocks, token JSON, and version constants.
CLI workflow and validation
tools/whatspec-codegen/src/main.rs, tools/whatspec-codegen/tests/*, .github/workflows/main.yml, Cargo.toml, src/version.rs
Adds generation, checking, formatting, descriptor validation, integration tests, workspace wiring, public version re-exports, and CI enforcement.
Updated generated artifacts
wacore/appstate/src/schemas.rs, wacore/binary/src/tokens.json, wacore/src/version/*, waproto/src/whatsapp.proto, waproto/src/whatsapp.desc.sha256, wacore/src/companion_reg.rs, wacore/src/store/device.rs
Updates protocol entities, AppState schemas, version constants, token output, checksums, platform mapping, and device version initialization.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: api-design, breaking-change

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the new code generator and the regeneration of artifacts at the specified WhatsApp version.
Description check ✅ Passed The description accurately explains the code generator, artifact regeneration, validation, CI changes, breaking changes, and implementation details.
Docstring Coverage ✅ Passed Docstring coverage is 97.73% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/whatspec-codegen-crate-mmum1o

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown

Greptile Summary

The PR introduces an owned, pinned whatspec code-generation pipeline and regenerates the protocol artifacts as one coherent set.

  • Adds emitters, IR validation, source acquisition, integrity checks, and offline consistency tests.
  • Generates and re-exports the WhatsApp Web version used by new devices.
  • Adds CI verification for generated artifacts, including the protobuf descriptor and its recorded hashes.
  • Updates the vendored protocol schemas, token tables, A/B properties, documentation, and descriptor.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the prior descriptor-drift issue is fixed because check mode now validates both the generated schema hash and committed descriptor hash.

Important Files Changed

Filename Overview
tools/whatspec-codegen/src/main.rs Coordinates pinned IR acquisition, coherent artifact generation, write/check modes, and descriptor hash validation; the previously reported check-mode gap is fixed.
tools/whatspec-codegen/src/source.rs Acquires pinned or local IR and verifies the lock revision and per-file digests.
tools/whatspec-codegen/src/emit/proto.rs Generates the vendored protobuf schema while preserving declarative local message additions.
tools/whatspec-codegen/tests/committed_artifacts.rs Adds offline tests enforcing common version stamps and token-table indexing invariants.
.github/workflows/main.yml Adds a bounded CI job that verifies all generated artifacts against the pinned IR.
waproto/src/whatsapp.proto Regenerates the WhatsApp protobuf schema from the newly pinned whatspec bundle.
wacore/src/version/generated.rs Adds generated constants tying the default announced client version to the vendored artifact version.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Pinned whatspec commit] --> B[Verify IR digests and version stamps]
  B --> C[Generate Rust, JSON, and proto artifacts]
  C --> D{Mode}
  D -->|write| E[Write generated files]
  E --> F[Run protoc and update descriptor hashes]
  D -->|check| G[Compare generated text artifacts]
  G --> H[Validate proto and descriptor hashes]
  H --> I[Generated Artifacts CI passes]
Loading

Reviews (4): Last reviewed commit: "fix(codegen): fail before writing when p..." | Re-trigger Greptile

Comment thread tools/whatspec-codegen/src/main.rs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53641afda8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/main.yml
Comment thread tools/whatspec-codegen/src/main.rs
Comment thread tools/whatspec-codegen/src/main.rs
Comment thread tools/whatspec-codegen/src/main.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/main.yml:
- Line 46: Update the actions/checkout step in the workflow to set
persist-credentials to false, ensuring the checkout does not store the
GITHUB_TOKEN while preserving the existing action version and job behavior.
- Around line 37-45: Add a timeout-minutes value to the generated job definition
alongside runs-on, limiting the vendored artifact validation workflow so stalled
fetches or whatspec-codegen builds cannot block the required check.
- Around line 46-51: Update the workflow steps around actions/checkout@v6 and
dtolnay/rust-toolchain@master to derive the toolchain from rust-toolchain.toml
after checkout, then pass the parsed channel to the action’s toolchain input
instead of hardcoding nightly-2026-06-16. Preserve the rustfmt component and
subsequent cargo run check.

In `@tools/whatspec-codegen/src/emit/abprops.rs`:
- Around line 46-60: Validate that ir.configs is sorted by module before the
grouping loop in the emitter containing module_idents and render_module. Reject
or assert any later config whose module precedes the previous module, so
interleaved modules fail loudly instead of producing duplicate unique_ident
values; preserve the existing grouping behavior for valid sorted input.

In `@tools/whatspec-codegen/src/emit/appstate.rs`:
- Around line 161-179: Update render_enum to generate variant identifiers
through the crate’s existing unique_ident mechanism, deduplicating pascal_case
collisions while preserving each value’s as_str mapping; remove the unnecessary
Clone bound from its values iterator. Ensure the collection call sites that
format Collection variants reuse the same deduplicated names rather than calling
pascal_case independently.

In `@tools/whatspec-codegen/src/emit/mex.rs`:
- Around line 29-31: Update the builder setup in the operation emission flow to
reserve the exact top-level names Variables and Response in Builder::by_name
before registering op.variables_shape and op.response, ensuring nested objects
receive suffixed names and the generated API always exposes these names.
- Around line 108-119: The emitters must make generated identifiers unique after
normalization. In tools/whatspec-codegen/src/emit/mex.rs:108-119, update
register to maintain a per-struct used_fields set and pass each rust_ident(key)
through unique_ident; apply the same fix in the TypeNode::Object branch at lines
138-142. In tools/whatspec-codegen/src/emit/appstate.rs:161-179, update
render_enum to pass each pascal_case(v) through unique_ident and reuse the
resulting variant map at the Collection:: formatting sites on lines 85 and 99.
- Around line 173-182: Update scalar_rust so the "number" tag maps to a
float-capable Rust type instead of i64, while preserving the existing boolean
mapping and String fallback for other tags.

In `@tools/whatspec-codegen/src/main.rs`:
- Around line 306-324: Update rustfmt to use a unique scratch file for each
invocation instead of the fixed scratch.rs path, ensuring concurrent processes
cannot overwrite one another’s input or formatted output. Generate the unique
filename within target/whatspec-codegen while preserving the existing rustfmt
execution and readback flow.
- Around line 122-153: Update parse_args to reject --from when combined with
--update-lock, alongside the existing --check/update-lock contradiction
validation. Ensure the combination returns an error before any lock update
proceeds, while preserving valid local-source and update-lock usage
independently.

In `@tools/whatspec-codegen/src/naming.rs`:
- Around line 10-16: Add "try" to the RUST_KEYWORDS list in naming.rs so JSON
keys named try use the existing raw-identifier conversion path and emit valid
Rust 2024 code.

In `@tools/whatspec-codegen/src/source.rs`:
- Around line 177-192: Update verify around the actual.get lookup to return a
contextual error when an IR_FILES entry is missing instead of panicking with
expect. Preserve the existing lock comparison and error reporting, and identify
the missing file using the rel symbol.
- Around line 154-174: Update resolve_rev to parse all git ls-remote output
entries and require an exact ref-name match rather than accepting the first line
or a ref-name tail match. Prefer the peeled ^{} SHA for annotated tags, reject
ambiguous or missing exact matches, and validate the selected SHA is exactly 40
hexadecimal characters before returning it.

In `@wacore/appstate/src/schemas.rs`:
- Line 1264: Update the generation source rather than editing generated
schemas.rs or protobuf output: restore device-capability coverage for
DEVICE_CAPABILITIES and DEVICE_CAPABILITIES_V2, or route unsupported actions
through schemas_unlisted.rs as appropriate. Inspect the emitter/IR and
value_enum_fields handling to restore the STATUS_PRIVACY mode and modes mappings
when required for decoding. Regenerate both artifacts and verify they agree with
the declarations in whatsapp.proto.

In `@wacore/src/companion_reg.rs`:
- Around line 114-115: Add P::WAIL to the platform cases exercised by
unconfirmed_platform_types_collapse_to_other, ensuring the test asserts it
collapses to C::OtherWebClient alongside the existing SMARTGLASSES case.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6f33869b-8add-43f7-b194-0f7651878eba

📥 Commits

Reviewing files that changed from the base of the PR and between 8e1b502 and 53641af.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (29)
  • .github/workflows/main.yml
  • AGENTS.md
  • Cargo.toml
  • agent_docs/wa_web_reference.md
  • src/version.rs
  • tools/whatspec-codegen/Cargo.toml
  • tools/whatspec-codegen/src/emit/abprops.rs
  • tools/whatspec-codegen/src/emit/appstate.rs
  • tools/whatspec-codegen/src/emit/mex.rs
  • tools/whatspec-codegen/src/emit/mod.rs
  • tools/whatspec-codegen/src/emit/proto.rs
  • tools/whatspec-codegen/src/emit/tokens.rs
  • tools/whatspec-codegen/src/emit/version.rs
  • tools/whatspec-codegen/src/ir.rs
  • tools/whatspec-codegen/src/main.rs
  • tools/whatspec-codegen/src/naming.rs
  • tools/whatspec-codegen/src/source.rs
  • tools/whatspec-codegen/tests/committed_artifacts.rs
  • tools/whatspec-codegen/whatspec.lock.json
  • wacore/appstate/src/schemas.rs
  • wacore/binary/src/tokens.json
  • wacore/src/companion_reg.rs
  • wacore/src/iq/abprops.rs
  • wacore/src/store/device.rs
  • wacore/src/version.rs
  • wacore/src/version/generated.rs
  • waproto/src/whatsapp.desc
  • waproto/src/whatsapp.desc.sha256
  • waproto/src/whatsapp.proto

Comment thread .github/workflows/main.yml
Comment thread .github/workflows/main.yml
Comment thread .github/workflows/main.yml
Comment thread tools/whatspec-codegen/src/emit/abprops.rs
Comment thread tools/whatspec-codegen/src/emit/appstate.rs Outdated
Comment thread tools/whatspec-codegen/src/naming.rs
Comment thread tools/whatspec-codegen/src/source.rs
Comment thread tools/whatspec-codegen/src/source.rs
Comment thread wacore/appstate/src/schemas.rs
Comment thread wacore/src/companion_reg.rs
…t name collisions

The Generated Artifacts job inherited the workflow's RUSTC_WRAPPER=sccache
without installing sccache, so cargo failed before it ran anything.

The emitter changes are guards, not corrections: the committed artifacts still
reproduce byte for byte, so none of these collisions occur in the pinned IR. All
of them would have produced a generated file that does not compile, or a silent
public-API rename, while the generator exited zero.

- mex: reserve `Variables` and `Response` before walking either shape. Children
  are interned before their parent, so a field named `response` would take the
  bare name and rename the operation's own struct to `Response2`.
- mex: route field names through `unique_ident`. `fooBar` and `foo_bar` are one
  Rust field, and a struct cannot declare it twice.
- appstate: deduplicate enum variants and reuse that map at every `Collection::`
  site, and reject an action naming a collection the IR never declared. A new
  `unique_type_ident` suffixes without the underscore, since `CriticalBlock_2`
  would trip `non_camel_case_types` in the generated file.
- abprops: reject an IR whose configs are not grouped by module, which would
  split one registry across `web` and `web_2`.
- naming: `try` is a reserved keyword; a JSON key named `try` emitted a field
  Rust 2024 rejects.
- source: resolve `--rev` against an exact ref name. `git ls-remote` matches
  ref-name tails, so `main` could pin `refs/heads/feature/main`; prefer the
  peeled sha for annotated tags and require 40 hex digits.
- source: `verify` returns an error rather than panicking on a missing IR file.
- main: reject `--from` with `--update-lock`, which would pin a remote rev
  against digests read from an unrelated local tree, and give rustfmt a
  per-process scratch file so two runs cannot swap each other's output.

Copy link
Copy Markdown
Collaborator Author

Pushed 01d4523.

The CI failure was mine: Generated Artifacts inherited the workflow-level RUSTC_WRAPPER=sccache without installing sccache, so cargo died before running anything. The job now sets sccache up like its siblings, and carries timeout-minutes and persist-credentials: false — the latter because this is the one job that shells out git fetch to a different repository and has no use for the checkout token.

Applied, with a test each. Worth stating plainly: these are guards, not corrections. --check still reproduces the committed artifacts byte for byte, so none of these collisions occur in the pinned IR. Each would have produced a generated file that does not compile, or a silent public-API rename, while the generator exited zero — which is the failure mode this tool exists to remove.

Finding What changed
Nested object steals Variables/Response Both names reserved before either shape is walked (a field inside variables_shape can want Response just as easily)
Field names skip unique_ident fooBar and foo_bar are one Rust field; routed through unique_ident per struct
render_enum variant collisions Deduplicated, and the resulting map reused at every Collection:: site. Needed a new unique_type_identunique_ident's _2 suffix would have made CriticalBlock_2 trip non_camel_case_types in the generated file
Undeclared Collection:: reference generate is fallible now and names the offending action
abprops not grouped by module Rejected, instead of splitting a registry across web and web_2
try missing from RUST_KEYWORDS Added
resolve_rev tail matching Exact ref-name match, peeled sha preferred for annotated tags, 40-hex-digit check
.expect() in verify Returns an error naming the file
--from with --update-lock Rejected
Shared rustfmt scratch file Per-process, and removed after read-back
P::WAIL untested Added to unconfirmed_platform_types_collapse_to_other

Not applied, three:

  1. number → float in scalar_rust. This would change the committed mex_operations.rs, turning every _count, _timestamp, _size, _index and _version field into f64. Those tags come from whatspec's name-suffix heuristic, and that heuristic's whole vocabulary is integral by construction — the analysis quoted the suffix list itself (_count, _time, _size, _duration). The 1.5 case is hypothetical; i64 is what the file has shipped and what those fields actually carry. Widening to f64 is a downgrade for every real field to guard a value the extractor never classifies.

  2. Restore DeviceCapabilities / STATUS_PRIVACY enum fields in schemas.rs. The premise is that the appstate registry should mirror what whatsapp.proto declares. It should not — the registry lists actions WA Web's WAWebSyncdConst.Actions table builds, while the proto declares the whole value union including actions only mobile clients emit. schemas_unlisted.rs is not a mirror of the proto either; its one entry carries evidence that whatsmeow and Baileys emit that exact index. Nothing in this client reads, sends or decodes device_capabilities, and there is no such evidence for it. On value_enum_fields: it is unconsumed metadata copied verbatim from the IR, so "restoring" it would mean hand-editing a generated file to disagree with its source.

  3. Derive the toolchain from rust-toolchain.toml. Correct that the action does not read it, but every job in main.yml hardcodes nightly-2026-06-16. Changing that convention is a sweep across the file and belongs in its own PR, not smuggled into the one job this PR adds.


Generated by Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/whatspec-codegen/src/emit/appstate.rs`:
- Around line 77-85: Preserve the variant map returned by render_enum for Scope
instead of discarding it with .0, and use that map when emitting each action’s
scope in the appstate generation flow. Replace the independent
pascal_case(&action.scope) formatting with a lookup that fails loudly if the
scope is missing, matching the existing Collection handling. Add a regression
test covering scope names that normalize to the same PascalCase variant and
remain distinct.

In `@tools/whatspec-codegen/src/main.rs`:
- Around line 395-396: Remove the duplicated rationale comment near the test
case in the relevant test function, leaving the test name and assertion to
describe the scenario. Preserve the rationale comment at the lock-validation
site around the existing validation logic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6abbd6b3-8150-4980-86dc-bd677c3619a9

📥 Commits

Reviewing files that changed from the base of the PR and between 53641af and 01d4523.

📒 Files selected for processing (8)
  • .github/workflows/main.yml
  • tools/whatspec-codegen/src/emit/abprops.rs
  • tools/whatspec-codegen/src/emit/appstate.rs
  • tools/whatspec-codegen/src/emit/mex.rs
  • tools/whatspec-codegen/src/main.rs
  • tools/whatspec-codegen/src/naming.rs
  • tools/whatspec-codegen/src/source.rs
  • wacore/src/companion_reg.rs

Comment thread tools/whatspec-codegen/src/emit/appstate.rs
Comment thread tools/whatspec-codegen/src/main.rs Outdated
…and check mode

Four findings from the Codex and Greptile reviews, all real, all in the part of
the tool that is supposed to make drift impossible.

- The version-coherence loop only read `index.json`, so `proto/WAProto.proto`
  was never compared against the manifest. It is the one domain with no JSON
  envelope, and therefore the one whose drift would reach `whatsapp.proto` with
  every other registry agreeing. Its `/// WhatsApp Version:` header is now part
  of the same check.
- `--update-lock` wrote the lock before `build()` ran any emitter validation, so
  a lost proto anchor or an invalid token table left the lock pinned to a build
  no committed artifact describes. The lock is now the last thing written.
- `--check` returned after comparing the six text artifacts, so a tree
  regenerated with `--skip-proto-desc`, or a hand-edited `.desc`, passed the
  Generated Artifacts job and only failed later when another job built
  `waproto`. Check mode now compares both hashes recorded in
  `whatsapp.desc.sha256`, which needs no protoc.
- appstate routed `Collection` through the deduplicated variant map but left
  `Scope` deriving its name independently. That is the worse half: two scopes
  normalizing alike compile fine and give one action the other's scope, and a
  scope escaped by `ensure_ident` (`self` to `Self_`) names a variant that was
  never declared.

The committed artifacts still reproduce byte for byte.
greptile-apps[bot]
greptile-apps Bot previously approved these changes Aug 12, 2026
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

📦 Binary size report

Metric main PR Δ
bin size (stripped) 10.08 MiB 10.16 MiB +84.75 KiB (+0.82%) 🚨
bin .text 8.07 MiB 8.15 MiB +78.69 KiB (+0.95%) 🚨
bin allocated (text+data+bss) 10.08 MiB 10.16 MiB +81.11 KiB (+0.79%) 🔺
llvm-lines wacore 533,726 538,872 +5,146 (+0.96%) 🔺
llvm-lines wacore copies 17,428 17,689 +261 (+1.50%) ⚠️
llvm-lines whatsapp-rust lib 766,615 768,982 +2,367 (+0.31%) 🔺
llvm-lines whatsapp-rust lib copies 23,863 23,996 +133 (+0.56%) 🔺
deps crates (Cargo.lock) 462 463 +1 (+0.22%) 🔺
.text per crate
Crate main PR Δ
.text whatsapp_rust 1.86 MiB 1.86 MiB +267 B (+0.01%) 🔺
.text wacore 692.31 KiB 695.65 KiB +3.33 KiB (+0.48%) 🔺
.text wacore_binary 81.61 KiB 81.61 KiB 0
.text wacore_libsignal 178.98 KiB 178.98 KiB 0
.text wacore_appstate 22.35 KiB 22.37 KiB +22 B (+0.10%) 🔺
.text wacore_noise 20.94 KiB 20.94 KiB 0
.text waproto 1.74 MiB 1.81 MiB +67.45 KiB (+3.78%) ⚠️
.text whatsapp_rust_sqlite_storage 540.56 KiB 540.57 KiB +2 B (+0.00%) 🔺
.text whatsapp_rust_tokio_transport 40.49 KiB 40.49 KiB 0
.text whatsapp_rust_ureq_http_client 12.68 KiB 12.68 KiB 0
.text std 994.83 KiB 1002.87 KiB +8.04 KiB (+0.81%) 🔺
.text other deps 1.90 MiB 1.90 MiB -391 B (-0.02%) 🔽
Top movers (cargo-bloat attribution)
Crate main PR Δ
waproto 1.74 MiB 1.81 MiB +67.45 KiB (+3.78%)
std 994.83 KiB 1002.87 KiB +8.04 KiB (+0.81%)
wacore 692.31 KiB 695.65 KiB +3.33 KiB (+0.48%)

🚨 Per-PR size budget exceeded (Δ stripped ≤ 64.00 KiB, Δ .text ≤ 32.00 KiB):

  • bin size (stripped): +84.75 KiB (+0.82%) exceeds the 64.00 KiB per-PR budget
  • bin .text: +78.69 KiB (+0.95%) exceeds the 32.00 KiB per-PR budget

The size-increase-ok label is set, so the gate is not enforced for this PR.

Baseline: 8e1b50291 (latest main run) · Head: 92d819306 · Graphs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf18d4d8bc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/whatspec-codegen/src/main.rs Outdated
Comment thread tools/whatspec-codegen/src/source.rs Outdated
…a partial IR cache

Two follow-up findings, both about a run that stops halfway leaving state a
retry cannot repair.

- `write` replaces `whatsapp.proto` but not the descriptor beside it, so
  discovering a missing protoc afterwards left the two halves describing
  different schemas, in a tree that does not build and that looks regenerated.
  protoc is now checked before anything is written.
- `fetch_pinned` reused a cache entry whose `generated/` directory merely
  existed. An interrupted checkout therefore poisoned the cache permanently:
  every later run skipped the fetch and failed reading the missing files, with
  no way out but deleting the directory by hand. Reuse now requires every IR
  file to be present.
@greptile-apps
greptile-apps Bot dismissed their stale review August 12, 2026 03:54

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@jlucaso1 jlucaso1 added size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning and removed api-design breaking-change labels Aug 12, 2026 — with Claude
@jlucaso1
jlucaso1 merged commit 874328f into main Aug 12, 2026
29 of 30 checks passed
@jlucaso1
jlucaso1 deleted the claude/whatspec-codegen-crate-mmum1o branch August 12, 2026 04:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants