Repository navigation
fix(platform-sdk): decode nullable open strings as scalars (ALIEN-1310) - #930
Merged
lilienblum merged 1 commit intoOct 8, 2026
Conversation
The full Platform client generated ObservedResourceSample.resourceTypeHint
(anyOf [string, null]) as a struct of flattened optional fields, which
rejects any string. The hosted manager therefore failed every Kubernetes
Operator reconcile that carried a resource type hint ("container"), and
those deployments stopped recording heartbeats.
Treat a non-nullable open string and null as disjoint, as was already
done for closed string enums, so the union becomes oneOf and generates a
scalar type.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
lilienblum
deleted the
dan/alien-1310-staging-kubernetes-remote-operator-registrations-stay
branch
October 8, 2026 03:35
alongubkin
added a commit
that referenced
this pull request
Oct 10, 2026
Fixes ALIEN-1384.
## Problem
`alien deployments events` and `alien managers events` fail on every
event with CLI 3.3.29 through 3.3.31:
```
Failed to parse response: invalid type: string "success", expected struct EventListItemResponseState
```
## Root cause
The server is correct: it returns `alien_core::EventState` (`"none"`,
`"started"`, `"success"`, `{"failed": {...}}`). When the Platform spec
was regenerated in #843, `Event.state` changed from `oneOf` to `anyOf`.
Progenitor turns `anyOf` into a struct of flattened optional fields, and
a struct cannot decode a plain string. #930 fixed a similar case (string
plus null), but not this one.
## Fix
`normalize_disjoint_tagged_unions` now also changes an `anyOf` to
`oneOf` when every branch is either a string enum or an object with
exactly one required property, and no tag repeats. When there is more
than one object branch, each must set `additionalProperties: false`, or
one value could match two branches. This is how serde encodes an
externally tagged enum, so at most one branch can match. typify then
generates `enum EventState { Failed { error }, None, Started, Success
}`. In the generated filtered client, the only other union that changes
is `SetupLinkEntryPoint.provider` (two string enums, request only, no
Alien caller). Every flattened struct left in that client has only
object branches.
Alternatives considered: opening `state` to untyped JSON (the CLI only
displays it) would also tolerate future states, but it drops typing for
SDK users and `EventState` has had the same four variants since the
start. Changing the platform spec back to `oneOf` would be undone by the
next regeneration.
## Proof
- New `tests/event_state.rs` decodes the event payload as the API
returns it, with every `alien_core::EventState` variant, through `Event`
(manager events) and `EventListItemResponse` (deployment events), and
checks the state round-trips. On main both tests fail with `invalid
type: string "none", expected struct EventState`; with the fix both
pass.
- A filter test checks that open object branches, repeated tags and
nullable branches stay `anyOf`.
- `cargo test -p alien-platform-api`, with and without `--features
full-api`, and the `alien-cli` event tests pass.
- CLI built from this branch, run against the staging API: `alien
deployments events <dep>` prints the table (`success` / `none` rows).
The released CLI fails on the same deployment with the error above.
Staging has no user-managed manager right now, so `managers events` is
covered by the test (same `Event` type).
## Rollout
CLI-only. Ships with the next CLI release; no server change.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes ALIEN-1310.
What changes for users
Kubernetes Remote Operators report heartbeats again through a hosted manager. Since the Platform client was regenerated in #843, every Kubernetes Operator sync failed in the manager:
The manager then never reconciled with the Platform API. New registrations stayed
pending, and existing deployments stopped recordinglastHeartbeatAt.Cause
In the regenerated spec,
ObservedResourceSample.resourceTypeHintisanyOf [string, null]. Progenitor generates a struct of flattened optionals foranyOf, and that struct rejects every non-null string. #843 already rewrote closed string enums plus null tooneOf, but not open strings. The Kubernetes observer always sends"container".Change
normalize_nullable_strings(renamed fromnormalize_nullable_string_enums) now treats any non-nullable string branch, open or enum, as disjoint from the null branch, so the union becomesoneOfand generates a scalar type. I scanned the generated full client: this was the only flattened scalar union. The filtered client is unaffected.Verification
tests/observed_inventory.rs(full-api) round-trips analien_core::ObservedInventoryBatchwithresourceTypeHint: "container"andNonethrough the generated type. Before the fix it fails with the error above; after the fix it passes.cargo test --locked -p alien-platform-apiandcargo test --locked -p alien-platform-api --features full-apiboth pass.Rollout
Managers built with this client decode the inventory again; no Operator change is needed.