ubx ship <proposal-id> actually runs. It owns
failure semantics end to end — nothing about how a real cloud apply can
partially fail, hang, or return an ambiguous result is left to the
provider layer or bolted on afterward. This is the reliability
differentiator the Architecture page’s trust chain
depends on: what applies is exactly what was signed, and the executor is
what makes that true even when the network isn’t cooperating.
Per-resource state machine
unknown_post_timeout exists because a network can fail after a
provider has already started applying a change — the executor never
assumes success or failure from a timeout alone. It reconciles by
querying the provider directly for the resource’s real current state
before deciding anything.
Idempotency, by contract
ubx ship is safe to re-run any number of times. Each resource’s
behavior on re-run depends on its last sealed state: applied is
skipped outright, failed retries from pending within a bounded retry
budget, still_unknown reconciles again before any new apply call is
made — never a blind re-apply on top of something still unresolved. A
proposal whose every resource already landed reports a genuine no-op and
writes nothing new.
Error taxonomy: retryable vs. terminal vs. stale
- Retryable — a transient signal that doesn’t rule out the change having landed (a deadline exceeded before a response, a transport reset). Feeds reconciliation and the retry budget.
- Terminal — a real, structured error from the provider itself. Ends that resource’s attempt immediately; a provider that already said an attribute is invalid won’t answer differently on an immediate retry.
- Stale — a freshness check failed: reality moved since this proposal was resolved. See Staleness — this is a distinct classification from both of the above, because nothing was rejected, the ground just shifted.
Scope grows deliberately, not implicitly
The executor’s first version shipped onlydrift_revert proposals — the
one kind where every value being written back is already fully
concrete, letting it skip a distinct plan phase and go straight to
apply. Shipping resolver-produced change proposals, where a value can
still be $computed at ship time, was a later, explicit amendment, not
an assumption the first version already covered it.
Full detail, the complete state machine and every amendment since:
docs/executor.md
in ubiquex.
