API Changelog
The bossanova.v1 OrchestratorService (see the API Reference) uses Stripe-style, date-based versioning. Each dated version pins a stable response shape: when the server starts serving a new value or behavior, it also registers a down-convert transform so callers pinned to an older version keep receiving the shape they were built against.
The Bossanova-Version header
Clients select an API version by sending a Bossanova-Version request header
containing a YYYY-MM-DD identifier (for example Bossanova-Version: 2026-07-11).
The resolved version is echoed back on the response.
- A request that negotiates the Current version runs zero transforms.
- A request pinned to an older version has the ordered chain of down-convert transforms applied so it observes the behavior that shipped with its version.
- A header-less caller pins to the Baseline — the oldest supported version — so it never silently shifts onto newer behavior. This matches the Stripe intent of "pin to the version you started on."
The current version is 2026-08-21. The engineering procedure for adding a new
version and its transform lives in the repository at docs/api-versioning.md
(view on GitHub).
Versions
Listed oldest to newest. Every version below is a member of the production registry served by the OrchestratorService.
2026-06-29 — Baseline
The launch baseline and the oldest supported version. It is the version assumed for header-less callers. No transforms run against a Baseline-shaped response beyond those introduced by later versions.
2026-07-04 — Orphaned session state
The OrchestratorService began serving SessionState value
SESSION_STATE_ORPHANED on Session.state, a terminal state for a headless run
that was killed by a daemon restart. Clients pinned to an older version — which
were built before this value existed — are down-converted to
SESSION_STATE_IMPLEMENTING_PLAN, the prior observable behavior.
2026-07-05 — Agent auth-failed attention reason
The OrchestratorService began serving AttentionReason value
ATTENTION_REASON_AGENT_AUTH_FAILED on Session.attention_status.reason, a new
attention reason surfaced when an agent's pane shows the login-required terminal
shape (for example "Not logged in" / "Please run /login"). Older clients, which
never saw this reason, are down-converted back to no attention reason.
2026-07-06 — Unmanaged account label and limited chat status
Two transforms shipped in this version:
- Unmanaged account label — restores the prior "System default" account label for older clients where the server now serves "Unmanaged local credentials" for the unbound rotation account.
- Limited chat status — maps
CHAT_STATUS_LIMITEDand the derived "usage-limited…" session display shape back to the prior idle-style behavior for older clients. In other words, the new chat status value is hidden from clients that predate it.
2026-07-11 — No-eligible-account rotation outcome
The OrchestratorService began serving RotationOutcome value
ROTATION_OUTCOME_STATUS_ONLY_NO_ELIGIBLE_ACCOUNT on
Session.rotation_events[].outcome, distinguishing "no active account to rotate
to" from the prior "agent cannot rotate". Clients pinned to an older version are
down-converted back to ROTATION_OUTCOME_STATUS_ONLY_NO_CAPABILITY, the prior
observable value.
2026-07-18 — Errored session display recolor
The OrchestratorService began serving the errored-recolor display shape for
orphaned/blocked sessions on Session.display_label / display_intent /
display_spinner. An errored session now keeps its real underlying status label
and spinner (a live "working" spinner, a pending "? question") but has its intent
recolored to DISPLAY_INTENT_DANGER so the error stays visible; a legitimate
terminal muted PR ("✓ merged" / "closed") is exempt and stays muted. Clients
pinned to an older version were built against the prior shapes — a fixed
"orphaned"/DANGER/no-spinner tuple for orphaned sessions and an un-recolored
base cascade for blocked sessions — so they are down-converted back to those.
2026-07-23 — Respawn-in-place rotation outcomes
The OrchestratorService began serving two RotationOutcome values on
Session.rotation_events[].outcome:
ROTATION_OUTCOME_RESPAWNED_SAME_ACCOUNT, audited when a pane that failed
authentication is stopped and respawned in place under the same account (the
account itself probes healthy), and ROTATION_OUTCOME_RESPAWN_CAP_EXHAUSTED,
audited when the per-chat respawn-in-place budget for the window is spent.
Clients pinned to an older version are down-converted back to
ROTATION_OUTCOME_STATUS_ONLY_NO_CAPABILITY, the prior observable value.
2026-08-03 — Agent-stalled attention reason
The OrchestratorService began serving AttentionReason value
ATTENTION_REASON_AGENT_STALLED on Session.attention_status.reason, a new
attention reason raised when a chat reports CHAT_STATUS_WORKING while its agent
has made no semantic progress for longer than its phase's threshold — a silently
dead turn behind a still-animating spinner. Like the auth-failed reason it is
only raised where the session had no other attention, so clients pinned to an
older version are down-converted back to no attention reason, the prior
"keeps spinning" behavior.
2026-08-04 — Waiting chat status
The OrchestratorService began serving ChatStatus value CHAT_STATUS_WAITING
on ChatStatusEntry.status, ChatStatusDelta.status and
SessionStatusEntry.status, alongside a new waiting_reason string on each of
those messages. A chat is waiting when it is blocked on an external event — a
registered GitHub callback, a background poll tick — rather than computing, and
waiting_reason carries a short human-readable explanation such as
awaiting checks_passed_ready on owner/repo#123 (it is empty for every other
status). Before this version such a chat reported CHAT_STATUS_WORKING, so
clients pinned to an older version are down-converted back to
CHAT_STATUS_WORKING with an empty waiting_reason.
2026-08-12 — Draft-PR failure ranks below live activity
The OrchestratorService stopped letting a session-level draft-PR-creation failure
claim the session's primary display composite while a chat is live. The
? PR failed label used to outrank every branch except a pending question and a
usage-limit banner, so a row could read ? PR failed while the chat underneath it
read working — two contradictory presents on one row. That branch now sits
immediately below the working branch, so Session.display_label,
Session.display_intent and Session.display_spinner carry the live label
(working, waiting, initializing, merging, archiving) whenever one
applies, and still report ? PR failed when nothing is live. Clients pinned to an
older version were built against the previous precedence, so they are
down-converted back to ? PR failed.
The failure itself is not hidden from a current client: it is surfaced as a session warning hint, and that hint is deliberately exempt from the accompanying recessive treatment because it is the row's only carrier of the failure.
2026-08-16 — A cron gate that could not run is no longer reported as gated
The OrchestratorService began distinguishing a cron gate that could not be
evaluated from one that ran and decided there was no work. Previously every
blocked fire — a gate that timed out, could not be launched, or that the shell
reported missing (exit 127) or not executable (exit 126) — was recorded as
gated and derived CRON_JOB_STATUS_GATED, a warning-styled "waiting, healthy"
value. A broken deployment therefore looked exactly like a quiet backlog.
A gate that could not run now serves CronJob.last_run_outcome gate_failed and
derives CronJob.last_run_status CRON_JOB_STATUS_FAILED, and RunCronJobNow
returns the matching gate_failed skip reason. A gate that ran and exited
non-zero for any other code keeps gated / CRON_JOB_STATUS_GATED, and still
blocks the fire. No new CronJobStatus enum value was added — the new outcome
reuses the existing CRON_JOB_STATUS_FAILED, so this is a change in the value
served rather than in the schema.
Clients pinned to an older version were built against the previous values, so they
are down-converted back to outcome gated, status CRON_JOB_STATUS_GATED, and
skip reason gated.
2026-08-20 — A session account switch stopped by its own deadline is no longer reported as ABORTED
The OrchestratorService began distinguishing a session account switch that ran
out of time from one that failed for an unknown reason. Previously the
daemon had no wire value for "a deadline stopped this", so a switch ended by its
own daemon-side respawn budget came back with no typed error code and
ProxySwitchSessionAccount answered ABORTED — the historical fallback for an
unmodelled failure.
ABORTED was not merely vaguer; it was actively misleading. It invites a retry,
and a request killed by its own deadline is exactly the request a caller must not
retry: re-sending restarts a budget that has already proven too short, and on an
at-least-once command stream it can stack a second switch on top of work the
first one may have partially completed.
A switch ended by its daemon-side budget now answers DEADLINE_EXCEEDED. A new
CommandResult.ErrorCode value, ERROR_CODE_DEADLINE_EXCEEDED, carries the
distinction over the daemon stream. No procedure or message field was added —
this is a change in the status code served on an existing procedure rather than
in the schema.
The change is scoped to the account switch alone. Other commands that fail with
an expired context are unaffected and still answer ABORTED. ProxySwitchSessionAccount's
own relay timeout — the orchestrator giving up waiting for the daemon — has
always answered DEADLINE_EXCEEDED and is unchanged at every version, including
for older clients.
Clients pinned to an older version were built when this case read as ABORTED,
so they are down-converted back to ABORTED with the failure message preserved.
2026-08-21 — Switch cancellation and result-ceiling behavior (Current)
The OrchestratorService began distinguishing a session account switch that the
caller canceled from one that failed for an unknown reason. Previously the
daemon had no wire value for "the caller canceled this", so a canceled switch
came back with no typed error code and ProxySwitchSessionAccount answered
ABORTED, the historical fallback for an unmodelled failure.
ABORTED invites a retry, which is the wrong signal for a request the caller
abandoned. Retrying can stack duplicate switch work on top of the partially
completed first attempt.
A switch canceled by its caller now answers CANCELED. A new
CommandResult.ErrorCode value, ERROR_CODE_CANCELED, carries the distinction
over the daemon stream. No procedure or message field was added — this is a
change in the status code served on an existing procedure rather than in the
schema.
The change is scoped to the account switch alone. Other daemon-scoped commands
that return the same typed code still answer ABORTED, and
ProxySwitchSessionAccount's own orchestrator-side cancellation path is
unchanged for every version.
Clients pinned to an older version were built when this case read as ABORTED,
so they are down-converted back to ABORTED with the failure message preserved.
ProxySwitchSessionAccount began using self-describing timeout text when bosso's
own 120-second result ceiling expires before a daemon verdict arrives. The status
code remains DEADLINE_EXCEEDED, but current clients now see that the account
switch may still be running, and that the request did not cancel or tear down the
switch. The caller should re-check the chat's account to see where it landed.
Previously this handler-owned result ceiling used the generic relay timeout text:
command timed out after 2m0s. Clients pinned to an older version were built
against that message, so they are down-converted back to the legacy text while
keeping DEADLINE_EXCEEDED.
See also
- API Reference — the generated OpenAPI reference for the OrchestratorService.
docs/api-versioning.mdin the repository — the engineering procedure for adding a new API version and down-convert transform.