Skip to content

Support Protobuf enums as identifier fields - #948

Merged
alexander-yevsyukov merged 16 commits into
masterfrom
enum-id
Jun 19, 2026
Merged

Support Protobuf enums as identifier fields#948
alexander-yevsyukov merged 16 commits into
masterfrom
enum-id

Conversation

@alexander-yevsyukov

@alexander-yevsyukov alexander-yevsyukov commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What

Adds support for Protobuf enums as identifier (ID) fields, and makes
io.spine.base.Identifier the single source of truth for which Protobuf field
types may serve as an ID.

Enum IDs

  • IdType.ENUM recognizes a Protobuf enum (a ProtocolMessageEnum) as a valid
    ID type: value/class/field matching, defaultValue (the number-zero constant),
    and packing to com.google.protobuf.EnumValue.
  • Identifier is enum-aware:
    • isEmpty treats the number-zero constant as empty — by convention it is
      the reserved "undefined" (null-like) value, so the implicit (required)
      check rejects it, just like "" for String and the default instance for a
      Message.
    • toString returns the constant name.
    • unpack(Any, Class) reconstructs the enum constant via TypeConverter.
    • The class-level "Supported types" Javadoc now lists Protobuf enums.

Single source of truth for supported ID types

  • New Identifier.isSupportedIdType(FieldDescriptorProto.Type) and an
    Identifier.isSupportedIdType(FieldDescriptor) overload, backed by one
    authoritative SUPPORTED_ID_TYPES set next to the "Supported types" docs:
    string, every 32/64-bit integer encoding, enum, and Message.
  • Previously this rule was duplicated in core-jvm-compiler (production code
    and its tests). Centralizing it on the documented gateway lets downstream
    tools delegate instead of maintaining their own copy.

Tests & fixtures

  • New TaskStatus enum + EnumFieldId message fixtures.
  • IdentifierTest / IdTypeTest cover enum classification, defaultValue,
    isEmpty, toString, pack/unpack round-trip, and the new
    isSupportedIdType predicate (per-type checks, a completeness guard over every
    FieldDescriptorProto.Type, the descriptor overload, and consistency with
    IdType.matchField).

Why

Enum-typed IDs are a requested ID shape. The zero enum value is reserved for the
"undefined" state, so it must be rejected as an empty ID. Hosting the
supported-type rule on Identifier (the documented gateway) removes a cross-repo
duplication and keeps the rule in one place.

Notes for reviewers

  • An enum packs into Any as EnumValue (name + number only — not the enum
    type), so the constant can only be restored when the class is known:
    unpack(Any, Class) does this; the class-less unpack(Any) returns the raw
    EnumValue (unchanged behavior).
  • isSupportedIdType classifies the type axis only — a repeated/map
    field is never an ID regardless of element type, and google.protobuf.Empty
    is handled separately by callers.
  • A companion change in core-jvm-compiler (RequiredIdReaction delegating to
    this gateway and accepting enums at compile time) will follow as a separate PR,
    gated on this change publishing.

🤖 Generated with Claude Code

Also in this branch: a config submodule update

This branch floats the shared config submodule to its latest commit, so the diff
also contains the build/infra files that config distributes into every consumer
repo. These are not part of the enum-ID work and are reviewed in the config
repository, not here (per the AGENTS.md code-review filter). They are included only
because the branch was rebuilt against current config.

A reviewer focused on the enum-ID change can limit attention to the base/ sources.

Copilot AI review requested due to automatic review settings June 18, 2026 20:32

@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: 65e0c1e0a0

ℹ️ 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 base/src/main/java/io/spine/base/IdType.java

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class support for using Protobuf enums as identifier (ID) values in base, and centralizes the “which Protobuf field types are eligible to be an ID” rule in Identifier. The PR also includes several build/tooling updates (Gradle repo scoping, license report cache invalidation on version changes, submodule bootstrap, and dependency/version bumps).

Changes:

  • Introduce IdType.ENUM and make Identifier enum-aware (isEmpty treats the number-zero constant as empty, toString returns constant name, enum pack/unpack support).
  • Add Identifier.isSupportedIdType(...) (type/descriptor overloads) backed by a single authoritative SUPPORTED_ID_TYPES set, plus tests/fixtures for enum IDs.
  • Update build/tooling and metadata files (repository content filtering, license report task inputs, init-submodules bootstrap script, dependency/version updates, docs/dependency report regeneration).

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
version.gradle.kts Bumps published snapshot version.
init-submodules Adds a bootstrap script to initialize missing submodules in fresh worktrees/shallow checkouts.
gradle.properties Expands Gradle daemon JVM args (incl. Error Prone-related --add-exports/--add-opens).
docs/dependencies/pom.xml Updates documented snapshot version.
docs/dependencies/dependencies.md Regenerates dependency/license report with new version/date.
buildSrc/src/test/kotlin/io/spine/gradle/report/pom/DependencyWriterSpec.kt Updates test configuration name used for dependency scope selection.
buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt Forces license report regeneration when project version changes (cache invalidation input).
buildSrc/src/main/kotlin/io/spine/gradle/repo/Repositories.kt Reorders/scopes repositories to reduce resolution failures and unnecessary lookups.
buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt Updates docs about gh-pages branch creation behavior.
buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt Seeds a newly created gh-pages branch with a CNAME file.
buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt Adds checkoutOrCreate and branch-seeding/push-adoption logic for docs publishing.
buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt Ensures process output reader threads are joined before evaluating buffers.
buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoTap.kt Bumps ProtoTap version.
buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt Bumps core-jvm-compiler dogfooding/test versions.
buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt Bumps core-jvm version.
buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt Bumps compiler fallback versions.
buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt Bumps base dependency versions used by build scripts.
base/src/test/proto/spine/test/identifiers_test.proto Adds TaskStatus enum + EnumFieldId fixture message.
base/src/test/kotlin/io/spine/base/IdTypeTest.kt Adds enum-to-EnumValue conversion test.
base/src/test/java/io/spine/base/IdentifierTest.java Adds enum ID tests and isSupportedIdType test coverage.
base/src/main/java/io/spine/base/IdType.java Adds ENUM ID type and zero-value enum default handling.
base/src/main/java/io/spine/base/Identifier.java Makes Identifier enum-aware and introduces isSupportedIdType(...) as the single source of truth.
AGENTS.md Documents init-submodules bootstrap and updates skip-list.
.gitmodules Adjusts submodule update strategy.
.claude/settings.json Adds a SessionStart hook to run init-submodules.

Comment thread base/src/main/java/io/spine/base/IdType.java
Comment thread base/src/main/java/io/spine/base/Identifier.java Outdated
@alexander-yevsyukov
alexander-yevsyukov enabled auto-merge (squash) June 18, 2026 20:37
`Field.findIdField` guarded only the `MESSAGE` case with `sameMessageType`, so
an enum ID search matched the first enum field regardless of its enum type —
for a message with two enum fields of different types, searching for one would
return the other.

Add a parallel `sameEnumType` guard so an enum ID field is matched by its
specific enum type, with a regression test over `TwoEnumFieldsId`, which
declares an unrelated enum field before the matching one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alexander-yevsyukov alexander-yevsyukov moved this to 🏗 In progress in v2.0 Jun 18, 2026
@alexander-yevsyukov alexander-yevsyukov self-assigned this Jun 18, 2026
`IdType.ENUM.matchClass` and `Identifier.unpack(Any, Class)` accepted any
`ProtocolMessageEnum`-assignable class, including the `ProtocolMessageEnum`
interface itself and other non-enum implementors. The interface has no enum
constants, so `defaultValue()` would fail on `getEnumConstants()`, and
`TypeConverter` cannot convert it.

Require `idClass.isEnum()` in both places (matching `ProtoConverter.isProtoEnum`),
with a regression test that the `ProtocolMessageEnum` interface is rejected as an
ID class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 18, 2026 20:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 1 comment.

Comment thread base/src/main/java/io/spine/base/IdType.java
@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.02%. Comparing base (88613b1) to head (285ba05).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #948      +/-   ##
==========================================
- Coverage   94.04%   94.02%   -0.03%     
==========================================
  Files         192      192              
  Lines        4149     4185      +36     
  Branches      339      346       +7     
==========================================
+ Hits         3902     3935      +33     
- Misses        147      149       +2     
- Partials      100      101       +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

alexander-yevsyukov and others added 2 commits June 18, 2026 21:55
`IdType.ENUM.matchValue` accepted any `ProtocolMessageEnum` instance, so a
non-enum implementor would be classified as an enum ID and later fail the
`(Enum<?>)` cast in `Identifier.toString()`. Require the value to be a Java
enum constant too, consistent with `matchClass`, with a regression test using
a non-enum `ProtocolMessageEnum` stub.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover the previously-untested enum-ID branches: rejecting a plain Java enum
(value, class, and unpack paths), the `UNRECOGNIZED` constant in `isEmpty`,
and the `ENUM.fromMessage` contract.

Simplify `IdType.zeroValue` to rely on the proto3 guarantee that the constant
with number zero is declared first — removing the unreachable UNRECOGNIZED-skip
and no-zero-throw branches and the duplicated `UNRECOGNIZED` literal. Collapse
`isUndefinedEnum` now that `matchValue` guarantees the value is a Java enum.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 18, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 2 comments.

Comment thread base/src/main/java/io/spine/base/IdType.java
Comment thread base/src/main/java/io/spine/base/Identifier.java
Per review, `zeroValue` relied on the proto3 rule that the number-zero
constant is declared first. Locate it by number through the enum descriptor
(`findValueByNumber(0)`) so it is correct regardless of declaration order,
falling back to the first constant only when the enum declares no zero value
(possible for proto2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alexander-yevsyukov
alexander-yevsyukov merged commit cd5e91f into master Jun 19, 2026
8 checks passed
@alexander-yevsyukov
alexander-yevsyukov deleted the enum-id branch June 19, 2026 08:04
@github-project-automation github-project-automation Bot moved this from 🏗 In progress to ✅ Done in v2.0 Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants